MediaWiki  REL1_19
Action.php
Go to the documentation of this file.
00001 <?php
00030 abstract class Action {
00031 
00036         protected $page;
00037 
00042         protected $context;
00043 
00048         protected $fields;
00049 
00057         private final static function getClass( $action, array $overrides ) {
00058                 global $wgActions;
00059                 $action = strtolower( $action );
00060 
00061                 if ( !isset( $wgActions[$action] ) ) {
00062                         return null;
00063                 }
00064 
00065                 if ( $wgActions[$action] === false ) {
00066                         return false;
00067                 } elseif ( $wgActions[$action] === true && isset( $overrides[$action] ) ) {
00068                         return $overrides[$action];
00069                 } elseif ( $wgActions[$action] === true ) {
00070                         return ucfirst( $action ) . 'Action';
00071                 } else {
00072                         return $wgActions[$action];
00073                 }
00074         }
00075 
00084         public final static function factory( $action, Page $page, IContextSource $context = null ) {
00085                 $class = self::getClass( $action, $page->getActionOverrides() );
00086                 if ( $class ) {
00087                         $obj = new $class( $page, $context );
00088                         return $obj;
00089                 }
00090                 return $class;
00091         }
00092 
00102         public final static function getActionName( IContextSource $context ) {
00103                 global $wgActions;
00104 
00105                 $request = $context->getRequest();
00106                 $actionName = $request->getVal( 'action', 'view' );
00107 
00108                 // Check for disabled actions
00109                 if ( isset( $wgActions[$actionName] ) && $wgActions[$actionName] === false ) {
00110                         $actionName = 'nosuchaction';
00111                 }
00112 
00113                 // Workaround for bug #20966: inability of IE to provide an action dependent
00114                 // on which submit button is clicked.
00115                 if ( $actionName === 'historysubmit' ) {
00116                         if ( $request->getBool( 'revisiondelete' ) ) {
00117                                 $actionName = 'revisiondelete';
00118                         } else {
00119                                 $actionName = 'view';
00120                         }
00121                 } elseif ( $actionName == 'editredlink' ) {
00122                         $actionName = 'edit';
00123                 }
00124 
00125                 // Trying to get a WikiPage for NS_SPECIAL etc. will result
00126                 // in WikiPage::factory throwing "Invalid or virtual namespace -1 given."
00127                 // For SpecialPages et al, default to action=view.
00128                 if ( !$context->canUseWikiPage() ) {
00129                         return 'view';
00130                 }
00131                 
00132                 $action = Action::factory( $actionName, $context->getWikiPage() );
00133                 if ( $action instanceof Action ) {
00134                         return $action->getName();
00135                 }
00136 
00137                 return 'nosuchaction';
00138         }
00139 
00146         public final static function exists( $name ) {
00147                 return self::getClass( $name, array() ) !== null;
00148         }
00149 
00154         public final function getContext() {
00155                 if ( $this->context instanceof IContextSource ) {
00156                         return $this->context;
00157                 }
00158                 return $this->page->getContext();
00159         }
00160 
00166         public final function getRequest() {
00167                 return $this->getContext()->getRequest();
00168         }
00169 
00175         public final function getOutput() {
00176                 return $this->getContext()->getOutput();
00177         }
00178 
00184         public final function getUser() {
00185                 return $this->getContext()->getUser();
00186         }
00187 
00193         public final function getSkin() {
00194                 return $this->getContext()->getSkin();
00195         }
00196 
00202         public final function getLanguage() {
00203                 return $this->getContext()->getLanguage();
00204         }
00205 
00212         public final function getLang() {
00213                 wfDeprecated( __METHOD__, '1.19' );
00214                 return $this->getLanguage();
00215         }
00216 
00221         public final function getTitle() {
00222                 return $this->page->getTitle();
00223         }
00224 
00231         public final function msg() {
00232                 $params = func_get_args();
00233                 return call_user_func_array( array( $this->getContext(), 'msg' ), $params );
00234         }
00235 
00242         protected function __construct( Page $page, IContextSource $context = null ) {
00243                 $this->page = $page;
00244                 $this->context = $context;
00245         }
00246 
00251         public abstract function getName();
00252 
00258         public function getRestriction() {
00259                 return null;
00260         }
00261 
00270         protected function checkCanExecute( User $user ) {
00271                 $right = $this->getRestriction();
00272                 if ( $right !== null ) {
00273                         $errors = $this->getTitle()->getUserPermissionsErrors( $right, $user );
00274                         if ( count( $errors ) ) {
00275                                 throw new PermissionsError( $right, $errors );
00276                         }
00277                 }
00278 
00279                 if ( $this->requiresUnblock() && $user->isBlocked() ) {
00280                         $block = $user->mBlock;
00281                         throw new UserBlockedError( $block );
00282                 }
00283 
00284                 // This should be checked at the end so that the user won't think the
00285                 // error is only temporary when he also don't have the rights to execute
00286                 // this action
00287                 if ( $this->requiresWrite() && wfReadOnly() ) {
00288                         throw new ReadOnlyError();
00289                 }
00290         }
00291 
00296         public function requiresWrite() {
00297                 return true;
00298         }
00299 
00304         public function requiresUnblock() {
00305                 return true;
00306         }
00307 
00312         protected function setHeaders() {
00313                 $out = $this->getOutput();
00314                 $out->setRobotPolicy( "noindex,nofollow" );
00315                 $out->setPageTitle( $this->getPageTitle() );
00316                 $this->getOutput()->setSubtitle( $this->getDescription() );
00317                 $out->setArticleRelated( true );
00318         }
00319 
00325         protected function getPageTitle() {
00326                 return $this->getTitle()->getPrefixedText();
00327         }
00328 
00334         protected function getDescription() {
00335                 return wfMsgHtml( strtolower( $this->getName() ) );
00336         }
00337 
00344         public abstract function show();
00345 
00350         public abstract function execute();
00351 }
00352 
00353 abstract class FormAction extends Action {
00354 
00359         protected abstract function getFormFields();
00360 
00365         protected function preText() { return ''; }
00366 
00370         protected function postText() { return ''; }
00371 
00376         protected function alterForm( HTMLForm $form ) {}
00377 
00382         protected function getForm() {
00383                 $this->fields = $this->getFormFields();
00384 
00385                 // Give hooks a chance to alter the form, adding extra fields or text etc
00386                 wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) );
00387 
00388                 $form = new HTMLForm( $this->fields, $this->getContext() );
00389                 $form->setSubmitCallback( array( $this, 'onSubmit' ) );
00390 
00391                 // Retain query parameters (uselang etc)
00392                 $form->addHiddenField( 'action', $this->getName() ); // Might not be the same as the query string
00393                 $params = array_diff_key(
00394                         $this->getRequest()->getQueryValues(),
00395                         array( 'action' => null, 'title' => null )
00396                 );
00397                 $form->addHiddenField( 'redirectparams', wfArrayToCGI( $params ) );
00398 
00399                 $form->addPreText( $this->preText() );
00400                 $form->addPostText( $this->postText() );
00401                 $this->alterForm( $form );
00402 
00403                 // Give hooks a chance to alter the form, adding extra fields or text etc
00404                 wfRunHooks( 'ActionBeforeFormDisplay', array( $this->getName(), &$form, $this->page ) );
00405 
00406                 return $form;
00407         }
00408 
00416         public abstract function onSubmit( $data );
00417 
00423         public abstract function onSuccess();
00424 
00432         public function show() {
00433                 $this->setHeaders();
00434 
00435                 // This will throw exceptions if there's a problem
00436                 $this->checkCanExecute( $this->getUser() );
00437 
00438                 $form = $this->getForm();
00439                 if ( $form->show() ) {
00440                         $this->onSuccess();
00441                 }
00442         }
00443 
00451         public function execute( array $data = null, $captureErrors = true ) {
00452                 try {
00453                         // Set a new context so output doesn't leak.
00454                         $this->context = clone $this->page->getContext();
00455 
00456                         // This will throw exceptions if there's a problem
00457                         $this->checkCanExecute( $this->getUser() );
00458 
00459                         $fields = array();
00460                         foreach ( $this->fields as $key => $params ) {
00461                                 if ( isset( $data[$key] ) ) {
00462                                         $fields[$key] = $data[$key];
00463                                 } elseif ( isset( $params['default'] ) ) {
00464                                         $fields[$key] = $params['default'];
00465                                 } else {
00466                                         $fields[$key] = null;
00467                                 }
00468                         }
00469                         $status = $this->onSubmit( $fields );
00470                         if ( $status === true ) {
00471                                 // This might do permanent stuff
00472                                 $this->onSuccess();
00473                                 return true;
00474                         } else {
00475                                 return false;
00476                         }
00477                 }
00478                 catch ( ErrorPageError $e ) {
00479                         if ( $captureErrors ) {
00480                                 return false;
00481                         } else {
00482                                 throw $e;
00483                         }
00484                 }
00485         }
00486 }
00487 
00493 abstract class FormlessAction extends Action {
00494 
00500         public abstract function onView();
00501 
00505         protected function getFormFields() {
00506                 return false;
00507         }
00508 
00509         public function onSubmit( $data ) {
00510                 return false;
00511         }
00512 
00513         public function onSuccess() {
00514                 return false;
00515         }
00516 
00517         public function show() {
00518                 $this->setHeaders();
00519 
00520                 // This will throw exceptions if there's a problem
00521                 $this->checkCanExecute( $this->getUser() );
00522 
00523                 $this->getOutput()->addHTML( $this->onView() );
00524         }
00525 
00533         public function execute( array $data = null, $captureErrors = true ) {
00534                 try {
00535                         // Set a new context so output doesn't leak.
00536                         $this->context = clone $this->page->getContext();
00537                         if ( is_array( $data ) ) {
00538                                 $this->context->setRequest( new FauxRequest( $data, false ) );
00539                         }
00540 
00541                         // This will throw exceptions if there's a problem
00542                         $this->checkCanExecute( $this->getUser() );
00543 
00544                         $this->onView();
00545                         return true;
00546                 }
00547                 catch ( ErrorPageError $e ) {
00548                         if ( $captureErrors ) {
00549                                 return false;
00550                         } else {
00551                                 throw $e;
00552                         }
00553                 }
00554         }
00555 }