MediaWiki  REL1_20
Action.php
Go to the documentation of this file.
00001 <?php
00037 abstract class Action {
00038 
00043         protected $page;
00044 
00049         protected $context;
00050 
00055         protected $fields;
00056 
00064         private final static function getClass( $action, array $overrides ) {
00065                 global $wgActions;
00066                 $action = strtolower( $action );
00067 
00068                 if ( !isset( $wgActions[$action] ) ) {
00069                         return null;
00070                 }
00071 
00072                 if ( $wgActions[$action] === false ) {
00073                         return false;
00074                 } elseif ( $wgActions[$action] === true && isset( $overrides[$action] ) ) {
00075                         return $overrides[$action];
00076                 } elseif ( $wgActions[$action] === true ) {
00077                         return ucfirst( $action ) . 'Action';
00078                 } else {
00079                         return $wgActions[$action];
00080                 }
00081         }
00082 
00091         public final static function factory( $action, Page $page, IContextSource $context = null ) {
00092                 $class = self::getClass( $action, $page->getActionOverrides() );
00093                 if ( $class ) {
00094                         $obj = new $class( $page, $context );
00095                         return $obj;
00096                 }
00097                 return $class;
00098         }
00099 
00109         public final static function getActionName( IContextSource $context ) {
00110                 global $wgActions;
00111 
00112                 $request = $context->getRequest();
00113                 $actionName = $request->getVal( 'action', 'view' );
00114 
00115                 // Check for disabled actions
00116                 if ( isset( $wgActions[$actionName] ) && $wgActions[$actionName] === false ) {
00117                         $actionName = 'nosuchaction';
00118                 }
00119 
00120                 // Workaround for bug #20966: inability of IE to provide an action dependent
00121                 // on which submit button is clicked.
00122                 if ( $actionName === 'historysubmit' ) {
00123                         if ( $request->getBool( 'revisiondelete' ) ) {
00124                                 $actionName = 'revisiondelete';
00125                         } else {
00126                                 $actionName = 'view';
00127                         }
00128                 } elseif ( $actionName == 'editredlink' ) {
00129                         $actionName = 'edit';
00130                 }
00131 
00132                 // Trying to get a WikiPage for NS_SPECIAL etc. will result
00133                 // in WikiPage::factory throwing "Invalid or virtual namespace -1 given."
00134                 // For SpecialPages et al, default to action=view.
00135                 if ( !$context->canUseWikiPage() ) {
00136                         return 'view';
00137                 }
00138 
00139                 $action = Action::factory( $actionName, $context->getWikiPage() );
00140                 if ( $action instanceof Action ) {
00141                         return $action->getName();
00142                 }
00143 
00144                 return 'nosuchaction';
00145         }
00146 
00153         public final static function exists( $name ) {
00154                 return self::getClass( $name, array() ) !== null;
00155         }
00156 
00161         public final function getContext() {
00162                 if ( $this->context instanceof IContextSource ) {
00163                         return $this->context;
00164                 }
00165                 return $this->page->getContext();
00166         }
00167 
00173         public final function getRequest() {
00174                 return $this->getContext()->getRequest();
00175         }
00176 
00182         public final function getOutput() {
00183                 return $this->getContext()->getOutput();
00184         }
00185 
00191         public final function getUser() {
00192                 return $this->getContext()->getUser();
00193         }
00194 
00200         public final function getSkin() {
00201                 return $this->getContext()->getSkin();
00202         }
00203 
00209         public final function getLanguage() {
00210                 return $this->getContext()->getLanguage();
00211         }
00212 
00219         public final function getLang() {
00220                 wfDeprecated( __METHOD__, '1.19' );
00221                 return $this->getLanguage();
00222         }
00223 
00228         public final function getTitle() {
00229                 return $this->page->getTitle();
00230         }
00231 
00238         public final function msg() {
00239                 $params = func_get_args();
00240                 return call_user_func_array( array( $this->getContext(), 'msg' ), $params );
00241         }
00242 
00249         protected function __construct( Page $page, IContextSource $context = null ) {
00250                 $this->page = $page;
00251                 $this->context = $context;
00252         }
00253 
00258         public abstract function getName();
00259 
00265         public function getRestriction() {
00266                 return null;
00267         }
00268 
00278         protected function checkCanExecute( User $user ) {
00279                 $right = $this->getRestriction();
00280                 if ( $right !== null ) {
00281                         $errors = $this->getTitle()->getUserPermissionsErrors( $right, $user );
00282                         if ( count( $errors ) ) {
00283                                 throw new PermissionsError( $right, $errors );
00284                         }
00285                 }
00286 
00287                 if ( $this->requiresUnblock() && $user->isBlocked() ) {
00288                         $block = $user->getBlock();
00289                         throw new UserBlockedError( $block );
00290                 }
00291 
00292                 // This should be checked at the end so that the user won't think the
00293                 // error is only temporary when he also don't have the rights to execute
00294                 // this action
00295                 if ( $this->requiresWrite() && wfReadOnly() ) {
00296                         throw new ReadOnlyError();
00297                 }
00298                 return true;
00299         }
00300 
00305         public function requiresWrite() {
00306                 return true;
00307         }
00308 
00313         public function requiresUnblock() {
00314                 return true;
00315         }
00316 
00321         protected function setHeaders() {
00322                 $out = $this->getOutput();
00323                 $out->setRobotPolicy( "noindex,nofollow" );
00324                 $out->setPageTitle( $this->getPageTitle() );
00325                 $this->getOutput()->setSubtitle( $this->getDescription() );
00326                 $out->setArticleRelated( true );
00327         }
00328 
00334         protected function getPageTitle() {
00335                 return $this->getTitle()->getPrefixedText();
00336         }
00337 
00343         protected function getDescription() {
00344                 return $this->msg( strtolower( $this->getName() ) )->escaped();
00345         }
00346 
00353         public abstract function show();
00354 
00359         public abstract function execute();
00360 }
00361 
00365 abstract class FormAction extends Action {
00366 
00371         protected abstract function getFormFields();
00372 
00377         protected function preText() { return ''; }
00378 
00382         protected function postText() { return ''; }
00383 
00388         protected function alterForm( HTMLForm $form ) {}
00389 
00394         protected function getForm() {
00395                 $this->fields = $this->getFormFields();
00396 
00397                 // Give hooks a chance to alter the form, adding extra fields or text etc
00398                 wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) );
00399 
00400                 $form = new HTMLForm( $this->fields, $this->getContext(), $this->getName() );
00401                 $form->setSubmitCallback( array( $this, 'onSubmit' ) );
00402 
00403                 // Retain query parameters (uselang etc)
00404                 $form->addHiddenField( 'action', $this->getName() ); // Might not be the same as the query string
00405                 $params = array_diff_key(
00406                         $this->getRequest()->getQueryValues(),
00407                         array( 'action' => null, 'title' => null )
00408                 );
00409                 $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) );
00410 
00411                 $form->addPreText( $this->preText() );
00412                 $form->addPostText( $this->postText() );
00413                 $this->alterForm( $form );
00414 
00415                 // Give hooks a chance to alter the form, adding extra fields or text etc
00416                 wfRunHooks( 'ActionBeforeFormDisplay', array( $this->getName(), &$form, $this->page ) );
00417 
00418                 return $form;
00419         }
00420 
00428         public abstract function onSubmit( $data );
00429 
00435         public abstract function onSuccess();
00436 
00444         public function show() {
00445                 $this->setHeaders();
00446 
00447                 // This will throw exceptions if there's a problem
00448                 $this->checkCanExecute( $this->getUser() );
00449 
00450                 $form = $this->getForm();
00451                 if ( $form->show() ) {
00452                         $this->onSuccess();
00453                 }
00454         }
00455 
00463         public function execute( array $data = null, $captureErrors = true ) {
00464                 try {
00465                         // Set a new context so output doesn't leak.
00466                         $this->context = clone $this->page->getContext();
00467 
00468                         // This will throw exceptions if there's a problem
00469                         $this->checkCanExecute( $this->getUser() );
00470 
00471                         $fields = array();
00472                         foreach ( $this->fields as $key => $params ) {
00473                                 if ( isset( $data[$key] ) ) {
00474                                         $fields[$key] = $data[$key];
00475                                 } elseif ( isset( $params['default'] ) ) {
00476                                         $fields[$key] = $params['default'];
00477                                 } else {
00478                                         $fields[$key] = null;
00479                                 }
00480                         }
00481                         $status = $this->onSubmit( $fields );
00482                         if ( $status === true ) {
00483                                 // This might do permanent stuff
00484                                 $this->onSuccess();
00485                                 return true;
00486                         } else {
00487                                 return false;
00488                         }
00489                 }
00490                 catch ( ErrorPageError $e ) {
00491                         if ( $captureErrors ) {
00492                                 return false;
00493                         } else {
00494                                 throw $e;
00495                         }
00496                 }
00497         }
00498 }
00499 
00503 abstract class FormlessAction extends Action {
00504 
00510         public abstract function onView();
00511 
00516         protected function getFormFields() {
00517                 return false;
00518         }
00519 
00524         public function onSubmit( $data ) {
00525                 return false;
00526         }
00527 
00531         public function onSuccess() {
00532                 return false;
00533         }
00534 
00535         public function show() {
00536                 $this->setHeaders();
00537 
00538                 // This will throw exceptions if there's a problem
00539                 $this->checkCanExecute( $this->getUser() );
00540 
00541                 $this->getOutput()->addHTML( $this->onView() );
00542         }
00543 
00551         public function execute( array $data = null, $captureErrors = true ) {
00552                 try {
00553                         // Set a new context so output doesn't leak.
00554                         $this->context = clone $this->page->getContext();
00555                         if ( is_array( $data ) ) {
00556                                 $this->context->setRequest( new FauxRequest( $data, false ) );
00557                         }
00558 
00559                         // This will throw exceptions if there's a problem
00560                         $this->checkCanExecute( $this->getUser() );
00561 
00562                         $this->onView();
00563                         return true;
00564                 }
00565                 catch ( ErrorPageError $e ) {
00566                         if ( $captureErrors ) {
00567                                 return false;
00568                         } else {
00569                                 throw $e;
00570                         }
00571                 }
00572         }
00573 }