MediaWiki
REL1_22
|
00001 <?php 00037 abstract class Action { 00038 00043 protected $page; 00044 00049 protected $context; 00050 00055 protected $fields; 00056 00064 final private 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 final public static function factory( $action, Page $page, IContextSource $context = null ) { 00092 $classOrCallable = self::getClass( $action, $page->getActionOverrides() ); 00093 00094 if ( is_string( $classOrCallable ) ) { 00095 $obj = new $classOrCallable( $page, $context ); 00096 return $obj; 00097 } 00098 00099 if ( is_callable( $classOrCallable ) ) { 00100 return call_user_func_array( $classOrCallable, array( $page, $context ) ); 00101 } 00102 00103 return $classOrCallable; 00104 } 00105 00115 final public static function getActionName( IContextSource $context ) { 00116 global $wgActions; 00117 00118 $request = $context->getRequest(); 00119 $actionName = $request->getVal( 'action', 'view' ); 00120 00121 // Check for disabled actions 00122 if ( isset( $wgActions[$actionName] ) && $wgActions[$actionName] === false ) { 00123 $actionName = 'nosuchaction'; 00124 } 00125 00126 // Workaround for bug #20966: inability of IE to provide an action dependent 00127 // on which submit button is clicked. 00128 if ( $actionName === 'historysubmit' ) { 00129 if ( $request->getBool( 'revisiondelete' ) ) { 00130 $actionName = 'revisiondelete'; 00131 } else { 00132 $actionName = 'view'; 00133 } 00134 } elseif ( $actionName == 'editredlink' ) { 00135 $actionName = 'edit'; 00136 } 00137 00138 // Trying to get a WikiPage for NS_SPECIAL etc. will result 00139 // in WikiPage::factory throwing "Invalid or virtual namespace -1 given." 00140 // For SpecialPages et al, default to action=view. 00141 if ( !$context->canUseWikiPage() ) { 00142 return 'view'; 00143 } 00144 00145 $action = Action::factory( $actionName, $context->getWikiPage(), $context ); 00146 if ( $action instanceof Action ) { 00147 return $action->getName(); 00148 } 00149 00150 return 'nosuchaction'; 00151 } 00152 00159 final public static function exists( $name ) { 00160 return self::getClass( $name, array() ) !== null; 00161 } 00162 00167 final public function getContext() { 00168 if ( $this->context instanceof IContextSource ) { 00169 return $this->context; 00170 } else if ( $this->page instanceof Article ) { 00171 // NOTE: $this->page can be a WikiPage, which does not have a context. 00172 wfDebug( __METHOD__ . ': no context known, falling back to Article\'s context.' ); 00173 return $this->page->getContext(); 00174 } 00175 00176 wfWarn( __METHOD__ . ': no context known, falling back to RequestContext::getMain().' ); 00177 return RequestContext::getMain(); 00178 } 00179 00185 final public function getRequest() { 00186 return $this->getContext()->getRequest(); 00187 } 00188 00194 final public function getOutput() { 00195 return $this->getContext()->getOutput(); 00196 } 00197 00203 final public function getUser() { 00204 return $this->getContext()->getUser(); 00205 } 00206 00212 final public function getSkin() { 00213 return $this->getContext()->getSkin(); 00214 } 00215 00221 final public function getLanguage() { 00222 return $this->getContext()->getLanguage(); 00223 } 00224 00231 final public function getLang() { 00232 wfDeprecated( __METHOD__, '1.19' ); 00233 return $this->getLanguage(); 00234 } 00235 00240 final public function getTitle() { 00241 return $this->page->getTitle(); 00242 } 00243 00250 final public function msg() { 00251 $params = func_get_args(); 00252 return call_user_func_array( array( $this->getContext(), 'msg' ), $params ); 00253 } 00254 00263 public function __construct( Page $page, IContextSource $context = null ) { 00264 if ( $context === null ) { 00265 wfWarn( __METHOD__ . ' called without providing a Context object.' ); 00266 // NOTE: We could try to initialize $context using $page->getContext(), 00267 // if $page is an Article. That however seems to not work seamlessly. 00268 } 00269 00270 $this->page = $page; 00271 $this->context = $context; 00272 } 00273 00278 abstract public function getName(); 00279 00285 public function getRestriction() { 00286 return null; 00287 } 00288 00298 protected function checkCanExecute( User $user ) { 00299 $right = $this->getRestriction(); 00300 if ( $right !== null ) { 00301 $errors = $this->getTitle()->getUserPermissionsErrors( $right, $user ); 00302 if ( count( $errors ) ) { 00303 throw new PermissionsError( $right, $errors ); 00304 } 00305 } 00306 00307 if ( $this->requiresUnblock() && $user->isBlocked() ) { 00308 $block = $user->getBlock(); 00309 throw new UserBlockedError( $block ); 00310 } 00311 00312 // This should be checked at the end so that the user won't think the 00313 // error is only temporary when he also don't have the rights to execute 00314 // this action 00315 if ( $this->requiresWrite() && wfReadOnly() ) { 00316 throw new ReadOnlyError(); 00317 } 00318 return true; 00319 } 00320 00325 public function requiresWrite() { 00326 return true; 00327 } 00328 00333 public function requiresUnblock() { 00334 return true; 00335 } 00336 00341 protected function setHeaders() { 00342 $out = $this->getOutput(); 00343 $out->setRobotPolicy( "noindex,nofollow" ); 00344 $out->setPageTitle( $this->getPageTitle() ); 00345 $this->getOutput()->setSubtitle( $this->getDescription() ); 00346 $out->setArticleRelated( true ); 00347 } 00348 00354 protected function getPageTitle() { 00355 return $this->getTitle()->getPrefixedText(); 00356 } 00357 00363 protected function getDescription() { 00364 return $this->msg( strtolower( $this->getName() ) )->escaped(); 00365 } 00366 00373 abstract public function show(); 00374 00379 abstract public function execute(); 00380 } 00381 00385 abstract class FormAction extends Action { 00386 00391 abstract protected function getFormFields(); 00392 00397 protected function preText() { 00398 return ''; 00399 } 00400 00404 protected function postText() { 00405 return ''; 00406 } 00407 00412 protected function alterForm( HTMLForm $form ) { 00413 } 00414 00419 protected function getForm() { 00420 $this->fields = $this->getFormFields(); 00421 00422 // Give hooks a chance to alter the form, adding extra fields or text etc 00423 wfRunHooks( 'ActionModifyFormFields', array( $this->getName(), &$this->fields, $this->page ) ); 00424 00425 $form = new HTMLForm( $this->fields, $this->getContext(), $this->getName() ); 00426 $form->setSubmitCallback( array( $this, 'onSubmit' ) ); 00427 00428 // Retain query parameters (uselang etc) 00429 $form->addHiddenField( 'action', $this->getName() ); // Might not be the same as the query string 00430 $params = array_diff_key( 00431 $this->getRequest()->getQueryValues(), 00432 array( 'action' => null, 'title' => null ) 00433 ); 00434 $form->addHiddenField( 'redirectparams', wfArrayToCgi( $params ) ); 00435 00436 $form->addPreText( $this->preText() ); 00437 $form->addPostText( $this->postText() ); 00438 $this->alterForm( $form ); 00439 00440 // Give hooks a chance to alter the form, adding extra fields or text etc 00441 wfRunHooks( 'ActionBeforeFormDisplay', array( $this->getName(), &$form, $this->page ) ); 00442 00443 return $form; 00444 } 00445 00453 abstract public function onSubmit( $data ); 00454 00460 abstract public function onSuccess(); 00461 00469 public function show() { 00470 $this->setHeaders(); 00471 00472 // This will throw exceptions if there's a problem 00473 $this->checkCanExecute( $this->getUser() ); 00474 00475 $form = $this->getForm(); 00476 if ( $form->show() ) { 00477 $this->onSuccess(); 00478 } 00479 } 00480 00489 public function execute( array $data = null, $captureErrors = true ) { 00490 try { 00491 // Set a new context so output doesn't leak. 00492 $this->context = clone $this->getContext(); 00493 00494 // This will throw exceptions if there's a problem 00495 $this->checkCanExecute( $this->getUser() ); 00496 00497 $fields = array(); 00498 foreach ( $this->fields as $key => $params ) { 00499 if ( isset( $data[$key] ) ) { 00500 $fields[$key] = $data[$key]; 00501 } elseif ( isset( $params['default'] ) ) { 00502 $fields[$key] = $params['default']; 00503 } else { 00504 $fields[$key] = null; 00505 } 00506 } 00507 $status = $this->onSubmit( $fields ); 00508 if ( $status === true ) { 00509 // This might do permanent stuff 00510 $this->onSuccess(); 00511 return true; 00512 } else { 00513 return false; 00514 } 00515 } 00516 catch ( ErrorPageError $e ) { 00517 if ( $captureErrors ) { 00518 return false; 00519 } else { 00520 throw $e; 00521 } 00522 } 00523 } 00524 } 00525 00529 abstract class FormlessAction extends Action { 00530 00536 abstract public function onView(); 00537 00542 protected function getFormFields() { 00543 return false; 00544 } 00545 00550 public function onSubmit( $data ) { 00551 return false; 00552 } 00553 00557 public function onSuccess() { 00558 return false; 00559 } 00560 00561 public function show() { 00562 $this->setHeaders(); 00563 00564 // This will throw exceptions if there's a problem 00565 $this->checkCanExecute( $this->getUser() ); 00566 00567 $this->getOutput()->addHTML( $this->onView() ); 00568 } 00569 00578 public function execute( array $data = null, $captureErrors = true ) { 00579 try { 00580 // Set a new context so output doesn't leak. 00581 $this->context = clone $this->getContext(); 00582 if ( is_array( $data ) ) { 00583 $this->context->setRequest( new FauxRequest( $data, false ) ); 00584 } 00585 00586 // This will throw exceptions if there's a problem 00587 $this->checkCanExecute( $this->getUser() ); 00588 00589 $this->onView(); 00590 return true; 00591 } 00592 catch ( ErrorPageError $e ) { 00593 if ( $captureErrors ) { 00594 return false; 00595 } else { 00596 throw $e; 00597 } 00598 } 00599 } 00600 }