MediaWiki
REL1_19
|
00001 <?php 00041 class ApiMain extends ApiBase { 00042 00046 const API_DEFAULT_FORMAT = 'xmlfm'; 00047 00051 private static $Modules = array( 00052 'login' => 'ApiLogin', 00053 'logout' => 'ApiLogout', 00054 'query' => 'ApiQuery', 00055 'expandtemplates' => 'ApiExpandTemplates', 00056 'parse' => 'ApiParse', 00057 'opensearch' => 'ApiOpenSearch', 00058 'feedcontributions' => 'ApiFeedContributions', 00059 'feedwatchlist' => 'ApiFeedWatchlist', 00060 'help' => 'ApiHelp', 00061 'paraminfo' => 'ApiParamInfo', 00062 'rsd' => 'ApiRsd', 00063 'compare' => 'ApiComparePages', 00064 00065 // Write modules 00066 'purge' => 'ApiPurge', 00067 'rollback' => 'ApiRollback', 00068 'delete' => 'ApiDelete', 00069 'undelete' => 'ApiUndelete', 00070 'protect' => 'ApiProtect', 00071 'block' => 'ApiBlock', 00072 'unblock' => 'ApiUnblock', 00073 'move' => 'ApiMove', 00074 'edit' => 'ApiEditPage', 00075 'upload' => 'ApiUpload', 00076 'filerevert' => 'ApiFileRevert', 00077 'emailuser' => 'ApiEmailUser', 00078 'watch' => 'ApiWatch', 00079 'patrol' => 'ApiPatrol', 00080 'import' => 'ApiImport', 00081 'userrights' => 'ApiUserrights', 00082 ); 00083 00087 private static $Formats = array( 00088 'json' => 'ApiFormatJson', 00089 'jsonfm' => 'ApiFormatJson', 00090 'php' => 'ApiFormatPhp', 00091 'phpfm' => 'ApiFormatPhp', 00092 'wddx' => 'ApiFormatWddx', 00093 'wddxfm' => 'ApiFormatWddx', 00094 'xml' => 'ApiFormatXml', 00095 'xmlfm' => 'ApiFormatXml', 00096 'yaml' => 'ApiFormatYaml', 00097 'yamlfm' => 'ApiFormatYaml', 00098 'rawfm' => 'ApiFormatJson', 00099 'txt' => 'ApiFormatTxt', 00100 'txtfm' => 'ApiFormatTxt', 00101 'dbg' => 'ApiFormatDbg', 00102 'dbgfm' => 'ApiFormatDbg', 00103 'dump' => 'ApiFormatDump', 00104 'dumpfm' => 'ApiFormatDump', 00105 ); 00106 00113 private static $mRights = array( 00114 'writeapi' => array( 00115 'msg' => 'Use of the write API', 00116 'params' => array() 00117 ), 00118 'apihighlimits' => array( 00119 'msg' => 'Use higher limits in API queries (Slow queries: $1 results; Fast queries: $2 results). The limits for slow queries also apply to multivalue parameters.', 00120 'params' => array( ApiBase::LIMIT_SML2, ApiBase::LIMIT_BIG2 ) 00121 ) 00122 ); 00123 00127 private $mPrinter; 00128 00129 private $mModules, $mModuleNames, $mFormats, $mFormatNames; 00130 private $mResult, $mAction, $mShowVersions, $mEnableWrite; 00131 private $mInternalMode, $mSquidMaxage, $mModule; 00132 00133 private $mCacheMode = 'private'; 00134 private $mCacheControl = array(); 00135 00142 public function __construct( $context = null, $enableWrite = false ) { 00143 if ( $context === null ) { 00144 $context = RequestContext::getMain(); 00145 } elseif ( $context instanceof WebRequest ) { 00146 // BC for pre-1.19 00147 $request = $context; 00148 $context = RequestContext::getMain(); 00149 } 00150 // We set a derivative context so we can change stuff later 00151 $this->setContext( new DerivativeContext( $context ) ); 00152 00153 if ( isset( $request ) ) { 00154 $this->getContext()->setRequest( $request ); 00155 } 00156 00157 $this->mInternalMode = ( $this->getRequest() instanceof FauxRequest ); 00158 00159 // Special handling for the main module: $parent === $this 00160 parent::__construct( $this, $this->mInternalMode ? 'main_int' : 'main' ); 00161 00162 if ( !$this->mInternalMode ) { 00163 // Impose module restrictions. 00164 // If the current user cannot read, 00165 // Remove all modules other than login 00166 global $wgUser; 00167 00168 if ( $this->getRequest()->getVal( 'callback' ) !== null ) { 00169 // JSON callback allows cross-site reads. 00170 // For safety, strip user credentials. 00171 wfDebug( "API: stripping user credentials for JSON callback\n" ); 00172 $wgUser = new User(); 00173 $this->getContext()->setUser( $wgUser ); 00174 } 00175 } 00176 00177 global $wgAPIModules; // extension modules 00178 $this->mModules = $wgAPIModules + self::$Modules; 00179 00180 $this->mModuleNames = array_keys( $this->mModules ); 00181 $this->mFormats = self::$Formats; 00182 $this->mFormatNames = array_keys( $this->mFormats ); 00183 00184 $this->mResult = new ApiResult( $this ); 00185 $this->mShowVersions = false; 00186 $this->mEnableWrite = $enableWrite; 00187 00188 $this->mSquidMaxage = - 1; // flag for executeActionWithErrorHandling() 00189 $this->mCommit = false; 00190 } 00191 00196 public function isInternalMode() { 00197 return $this->mInternalMode; 00198 } 00199 00205 public function getResult() { 00206 return $this->mResult; 00207 } 00208 00214 public function getModule() { 00215 return $this->mModule; 00216 } 00217 00223 public function getPrinter() { 00224 return $this->mPrinter; 00225 } 00226 00232 public function setCacheMaxAge( $maxage ) { 00233 $this->setCacheControl( array( 00234 'max-age' => $maxage, 00235 's-maxage' => $maxage 00236 ) ); 00237 } 00238 00264 public function setCacheMode( $mode ) { 00265 if ( !in_array( $mode, array( 'private', 'public', 'anon-public-user-private' ) ) ) { 00266 wfDebug( __METHOD__ . ": unrecognised cache mode \"$mode\"\n" ); 00267 // Ignore for forwards-compatibility 00268 return; 00269 } 00270 00271 if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { 00272 // Private wiki, only private headers 00273 if ( $mode !== 'private' ) { 00274 wfDebug( __METHOD__ . ": ignoring request for $mode cache mode, private wiki\n" ); 00275 return; 00276 } 00277 } 00278 00279 wfDebug( __METHOD__ . ": setting cache mode $mode\n" ); 00280 $this->mCacheMode = $mode; 00281 } 00282 00288 public function setCachePrivate() { 00289 wfDeprecated( __METHOD__, '1.17' ); 00290 $this->setCacheMode( 'private' ); 00291 } 00292 00303 public function setCacheControl( $directives ) { 00304 $this->mCacheControl = $directives + $this->mCacheControl; 00305 } 00306 00317 public function setVaryCookie() { 00318 wfDeprecated( __METHOD__, '1.17' ); 00319 $this->setCacheMode( 'anon-public-user-private' ); 00320 } 00321 00329 public function createPrinterByName( $format ) { 00330 if ( !isset( $this->mFormats[$format] ) ) { 00331 $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' ); 00332 } 00333 return new $this->mFormats[$format] ( $this, $format ); 00334 } 00335 00339 public function execute() { 00340 $this->profileIn(); 00341 if ( $this->mInternalMode ) { 00342 $this->executeAction(); 00343 } else { 00344 $this->executeActionWithErrorHandling(); 00345 } 00346 00347 $this->profileOut(); 00348 } 00349 00354 protected function executeActionWithErrorHandling() { 00355 // In case an error occurs during data output, 00356 // clear the output buffer and print just the error information 00357 ob_start(); 00358 00359 try { 00360 $this->executeAction(); 00361 } catch ( Exception $e ) { 00362 // Log it 00363 if ( $e instanceof MWException ) { 00364 wfDebugLog( 'exception', $e->getLogMessage() ); 00365 } 00366 00367 // Handle any kind of exception by outputing properly formatted error message. 00368 // If this fails, an unhandled exception should be thrown so that global error 00369 // handler will process and log it. 00370 00371 $errCode = $this->substituteResultWithError( $e ); 00372 00373 // Error results should not be cached 00374 $this->setCacheMode( 'private' ); 00375 00376 $response = $this->getRequest()->response(); 00377 $headerStr = 'MediaWiki-API-Error: ' . $errCode; 00378 if ( $e->getCode() === 0 ) { 00379 $response->header( $headerStr ); 00380 } else { 00381 $response->header( $headerStr, true, $e->getCode() ); 00382 } 00383 00384 // Reset and print just the error message 00385 ob_clean(); 00386 00387 // If the error occured during printing, do a printer->profileOut() 00388 $this->mPrinter->safeProfileOut(); 00389 $this->printResult( true ); 00390 } 00391 00392 // Send cache headers after any code which might generate an error, to 00393 // avoid sending public cache headers for errors. 00394 $this->sendCacheHeaders(); 00395 00396 if ( $this->mPrinter->getIsHtml() && !$this->mPrinter->isDisabled() ) { 00397 echo wfReportTime(); 00398 } 00399 00400 ob_end_flush(); 00401 } 00402 00403 protected function sendCacheHeaders() { 00404 global $wgUseXVO, $wgVaryOnXFP; 00405 $response = $this->getRequest()->response(); 00406 00407 if ( $this->mCacheMode == 'private' ) { 00408 $response->header( 'Cache-Control: private' ); 00409 return; 00410 } 00411 00412 if ( $this->mCacheMode == 'anon-public-user-private' ) { 00413 $xfp = $wgVaryOnXFP ? ', X-Forwarded-Proto' : ''; 00414 $response->header( 'Vary: Accept-Encoding, Cookie' . $xfp ); 00415 if ( $wgUseXVO ) { 00416 $out = $this->getOutput(); 00417 if ( $wgVaryOnXFP ) { 00418 $out->addVaryHeader( 'X-Forwarded-Proto' ); 00419 } 00420 $response->header( $out->getXVO() ); 00421 if ( $out->haveCacheVaryCookies() ) { 00422 // Logged in, mark this request private 00423 $response->header( 'Cache-Control: private' ); 00424 return; 00425 } 00426 // Logged out, send normal public headers below 00427 } elseif ( session_id() != '' ) { 00428 // Logged in or otherwise has session (e.g. anonymous users who have edited) 00429 // Mark request private 00430 $response->header( 'Cache-Control: private' ); 00431 return; 00432 } // else no XVO and anonymous, send public headers below 00433 } 00434 00435 // Send public headers 00436 if ( $wgVaryOnXFP ) { 00437 $response->header( 'Vary: Accept-Encoding, X-Forwarded-Proto' ); 00438 if ( $wgUseXVO ) { 00439 // Bleeeeegh. Our header setting system sucks 00440 $response->header( 'X-Vary-Options: Accept-Encoding;list-contains=gzip, X-Forwarded-Proto' ); 00441 } 00442 } 00443 00444 // If nobody called setCacheMaxAge(), use the (s)maxage parameters 00445 if ( !isset( $this->mCacheControl['s-maxage'] ) ) { 00446 $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' ); 00447 } 00448 if ( !isset( $this->mCacheControl['max-age'] ) ) { 00449 $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' ); 00450 } 00451 00452 if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) { 00453 // Public cache not requested 00454 // Sending a Vary header in this case is harmless, and protects us 00455 // against conditional calls of setCacheMaxAge(). 00456 $response->header( 'Cache-Control: private' ); 00457 return; 00458 } 00459 00460 $this->mCacheControl['public'] = true; 00461 00462 // Send an Expires header 00463 $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] ); 00464 $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge ); 00465 $response->header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expiryUnixTime ) ); 00466 00467 // Construct the Cache-Control header 00468 $ccHeader = ''; 00469 $separator = ''; 00470 foreach ( $this->mCacheControl as $name => $value ) { 00471 if ( is_bool( $value ) ) { 00472 if ( $value ) { 00473 $ccHeader .= $separator . $name; 00474 $separator = ', '; 00475 } 00476 } else { 00477 $ccHeader .= $separator . "$name=$value"; 00478 $separator = ', '; 00479 } 00480 } 00481 00482 $response->header( "Cache-Control: $ccHeader" ); 00483 } 00484 00491 protected function substituteResultWithError( $e ) { 00492 global $wgShowHostnames; 00493 00494 $result = $this->getResult(); 00495 // Printer may not be initialized if the extractRequestParams() fails for the main module 00496 if ( !isset ( $this->mPrinter ) ) { 00497 // The printer has not been created yet. Try to manually get formatter value. 00498 $value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT ); 00499 if ( !in_array( $value, $this->mFormatNames ) ) { 00500 $value = self::API_DEFAULT_FORMAT; 00501 } 00502 00503 $this->mPrinter = $this->createPrinterByName( $value ); 00504 if ( $this->mPrinter->getNeedsRawData() ) { 00505 $result->setRawMode(); 00506 } 00507 } 00508 00509 if ( $e instanceof UsageException ) { 00510 // User entered incorrect parameters - print usage screen 00511 $errMessage = $e->getMessageArray(); 00512 00513 // Only print the help message when this is for the developer, not runtime 00514 if ( $this->mPrinter->getWantsHelp() || $this->mAction == 'help' ) { 00515 ApiResult::setContent( $errMessage, $this->makeHelpMsg() ); 00516 } 00517 00518 } else { 00519 global $wgShowSQLErrors, $wgShowExceptionDetails; 00520 // Something is seriously wrong 00521 if ( ( $e instanceof DBQueryError ) && !$wgShowSQLErrors ) { 00522 $info = 'Database query error'; 00523 } else { 00524 $info = "Exception Caught: {$e->getMessage()}"; 00525 } 00526 00527 $errMessage = array( 00528 'code' => 'internal_api_error_' . get_class( $e ), 00529 'info' => $info, 00530 ); 00531 ApiResult::setContent( $errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : '' ); 00532 } 00533 00534 $result->reset(); 00535 $result->disableSizeCheck(); 00536 // Re-add the id 00537 $requestid = $this->getParameter( 'requestid' ); 00538 if ( !is_null( $requestid ) ) { 00539 $result->addValue( null, 'requestid', $requestid ); 00540 } 00541 00542 if ( $wgShowHostnames ) { 00543 // servedby is especially useful when debugging errors 00544 $result->addValue( null, 'servedby', wfHostName() ); 00545 } 00546 00547 $result->addValue( null, 'error', $errMessage ); 00548 00549 return $errMessage['code']; 00550 } 00551 00556 protected function setupExecuteAction() { 00557 global $wgShowHostnames; 00558 00559 // First add the id to the top element 00560 $result = $this->getResult(); 00561 $requestid = $this->getParameter( 'requestid' ); 00562 if ( !is_null( $requestid ) ) { 00563 $result->addValue( null, 'requestid', $requestid ); 00564 } 00565 00566 if ( $wgShowHostnames ) { 00567 $servedby = $this->getParameter( 'servedby' ); 00568 if ( $servedby ) { 00569 $result->addValue( null, 'servedby', wfHostName() ); 00570 } 00571 } 00572 00573 $params = $this->extractRequestParams(); 00574 00575 $this->mShowVersions = $params['version']; 00576 $this->mAction = $params['action']; 00577 00578 if ( !is_string( $this->mAction ) ) { 00579 $this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' ); 00580 } 00581 00582 return $params; 00583 } 00584 00589 protected function setupModule() { 00590 // Instantiate the module requested by the user 00591 $module = new $this->mModules[$this->mAction] ( $this, $this->mAction ); 00592 $this->mModule = $module; 00593 00594 $moduleParams = $module->extractRequestParams(); 00595 00596 // Die if token required, but not provided (unless there is a gettoken parameter) 00597 if ( isset( $moduleParams['gettoken'] ) ) { 00598 $gettoken = $moduleParams['gettoken']; 00599 } else { 00600 $gettoken = false; 00601 } 00602 00603 $salt = $module->getTokenSalt(); 00604 if ( $salt !== false && !$gettoken ) { 00605 if ( !isset( $moduleParams['token'] ) ) { 00606 $this->dieUsageMsg( array( 'missingparam', 'token' ) ); 00607 } else { 00608 if ( !$this->getUser()->matchEditToken( $moduleParams['token'], $salt, $this->getRequest() ) ) { 00609 $this->dieUsageMsg( 'sessionfailure' ); 00610 } 00611 } 00612 } 00613 return $module; 00614 } 00615 00622 protected function checkMaxLag( $module, $params ) { 00623 if ( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) { 00624 // Check for maxlag 00625 global $wgShowHostnames; 00626 $maxLag = $params['maxlag']; 00627 list( $host, $lag ) = wfGetLB()->getMaxLag(); 00628 if ( $lag > $maxLag ) { 00629 $response = $this->getRequest()->response(); 00630 00631 $response->header( 'Retry-After: ' . max( intval( $maxLag ), 5 ) ); 00632 $response->header( 'X-Database-Lag: ' . intval( $lag ) ); 00633 00634 if ( $wgShowHostnames ) { 00635 $this->dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' ); 00636 } else { 00637 $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' ); 00638 } 00639 return false; 00640 } 00641 } 00642 return true; 00643 } 00644 00649 protected function checkExecutePermissions( $module ) { 00650 $user = $this->getUser(); 00651 if ( $module->isReadMode() && !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) && 00652 !$user->isAllowed( 'read' ) ) 00653 { 00654 $this->dieUsageMsg( 'readrequired' ); 00655 } 00656 if ( $module->isWriteMode() ) { 00657 if ( !$this->mEnableWrite ) { 00658 $this->dieUsageMsg( 'writedisabled' ); 00659 } 00660 if ( !$user->isAllowed( 'writeapi' ) ) { 00661 $this->dieUsageMsg( 'writerequired' ); 00662 } 00663 if ( wfReadOnly() ) { 00664 $this->dieReadOnly(); 00665 } 00666 } 00667 } 00668 00674 protected function setupExternalResponse( $module, $params ) { 00675 // Ignore mustBePosted() for internal calls 00676 if ( $module->mustBePosted() && !$this->getRequest()->wasPosted() ) { 00677 $this->dieUsageMsg( array( 'mustbeposted', $this->mAction ) ); 00678 } 00679 00680 // See if custom printer is used 00681 $this->mPrinter = $module->getCustomPrinter(); 00682 if ( is_null( $this->mPrinter ) ) { 00683 // Create an appropriate printer 00684 $this->mPrinter = $this->createPrinterByName( $params['format'] ); 00685 } 00686 00687 if ( $this->mPrinter->getNeedsRawData() ) { 00688 $this->getResult()->setRawMode(); 00689 } 00690 } 00691 00695 protected function executeAction() { 00696 $params = $this->setupExecuteAction(); 00697 $module = $this->setupModule(); 00698 00699 $this->checkExecutePermissions( $module ); 00700 00701 if ( !$this->checkMaxLag( $module, $params ) ) { 00702 return; 00703 } 00704 00705 if ( !$this->mInternalMode ) { 00706 $this->setupExternalResponse( $module, $params ); 00707 } 00708 00709 // Execute 00710 $module->profileIn(); 00711 $module->execute(); 00712 wfRunHooks( 'APIAfterExecute', array( &$module ) ); 00713 $module->profileOut(); 00714 00715 if ( !$this->mInternalMode ) { 00716 // Print result data 00717 $this->printResult( false ); 00718 } 00719 } 00720 00726 protected function printResult( $isError ) { 00727 $this->getResult()->cleanUpUTF8(); 00728 $printer = $this->mPrinter; 00729 $printer->profileIn(); 00730 00736 $printer->setUnescapeAmps( ( $this->mAction == 'help' || $isError ) 00737 && $printer->getFormat() == 'XML' && $printer->getIsHtml() ); 00738 00739 $printer->initPrinter( $isError ); 00740 00741 $printer->execute(); 00742 $printer->closePrinter(); 00743 $printer->profileOut(); 00744 } 00745 00749 public function isReadMode() { 00750 return false; 00751 } 00752 00758 public function getAllowedParams() { 00759 return array( 00760 'format' => array( 00761 ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT, 00762 ApiBase::PARAM_TYPE => $this->mFormatNames 00763 ), 00764 'action' => array( 00765 ApiBase::PARAM_DFLT => 'help', 00766 ApiBase::PARAM_TYPE => $this->mModuleNames 00767 ), 00768 'version' => false, 00769 'maxlag' => array( 00770 ApiBase::PARAM_TYPE => 'integer' 00771 ), 00772 'smaxage' => array( 00773 ApiBase::PARAM_TYPE => 'integer', 00774 ApiBase::PARAM_DFLT => 0 00775 ), 00776 'maxage' => array( 00777 ApiBase::PARAM_TYPE => 'integer', 00778 ApiBase::PARAM_DFLT => 0 00779 ), 00780 'requestid' => null, 00781 'servedby' => false, 00782 ); 00783 } 00784 00790 public function getParamDescription() { 00791 return array( 00792 'format' => 'The format of the output', 00793 'action' => 'What action you would like to perform. See below for module help', 00794 'version' => 'When showing help, include version for each module', 00795 'maxlag' => array( 00796 'Maximum lag can be used when MediaWiki is installed on a database replicated cluster.', 00797 'To save actions causing any more site replication lag, this parameter can make the client', 00798 'wait until the replication lag is less than the specified value.', 00799 'In case of a replag error, a HTTP 503 error is returned, with the message like', 00800 '"Waiting for $host: $lag seconds lagged\n".', 00801 'See https://www.mediawiki.org/wiki/Manual:Maxlag_parameter for more information', 00802 ), 00803 'smaxage' => 'Set the s-maxage header to this many seconds. Errors are never cached', 00804 'maxage' => 'Set the max-age header to this many seconds. Errors are never cached', 00805 'requestid' => 'Request ID to distinguish requests. This will just be output back to you', 00806 'servedby' => 'Include the hostname that served the request in the results. Unconditionally shown on error', 00807 ); 00808 } 00809 00815 public function getDescription() { 00816 return array( 00817 '', 00818 '', 00819 '**********************************************************************************************************', 00820 '** **', 00821 '** This is an auto-generated MediaWiki API documentation page **', 00822 '** **', 00823 '** Documentation and Examples: **', 00824 '** https://www.mediawiki.org/wiki/API **', 00825 '** **', 00826 '**********************************************************************************************************', 00827 '', 00828 'Status: All features shown on this page should be working, but the API', 00829 ' is still in active development, and may change at any time.', 00830 ' Make sure to monitor our mailing list for any updates', 00831 '', 00832 'Erroneous requests: When erroneous requests are sent to the API, a HTTP header will be sent', 00833 ' with the key "MediaWiki-API-Error" and then both the value of the', 00834 ' header and the error code sent back will be set to the same value', 00835 '', 00836 ' In the case of an invalid action being passed, these will have a value', 00837 ' of "unknown_action"', 00838 '', 00839 ' For more information see https://www.mediawiki.org/wiki/API:Errors_and_warnings', 00840 '', 00841 'Documentation: https://www.mediawiki.org/wiki/API:Main_page', 00842 'FAQ https://www.mediawiki.org/wiki/API:FAQ', 00843 'Mailing list: https://lists.wikimedia.org/mailman/listinfo/mediawiki-api', 00844 'Api Announcements: https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce', 00845 'Bugs & Requests: https://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts', 00846 '', 00847 '', 00848 '', 00849 '', 00850 '', 00851 ); 00852 } 00853 00857 public function getPossibleErrors() { 00858 return array_merge( parent::getPossibleErrors(), array( 00859 array( 'readonlytext' ), 00860 array( 'code' => 'unknown_format', 'info' => 'Unrecognized format: format' ), 00861 array( 'code' => 'unknown_action', 'info' => 'The API requires a valid action parameter' ), 00862 array( 'code' => 'maxlag', 'info' => 'Waiting for host: x seconds lagged' ), 00863 array( 'code' => 'maxlag', 'info' => 'Waiting for a database server: x seconds lagged' ), 00864 ) ); 00865 } 00866 00871 protected function getCredits() { 00872 return array( 00873 'API developers:', 00874 ' Roan Kattouw <Firstname>.<Lastname>@gmail.com (lead developer Sep 2007-present)', 00875 ' Victor Vasiliev - vasilvv at gee mail dot com', 00876 ' Bryan Tong Minh - bryan . tongminh @ gmail . com', 00877 ' Sam Reed - sam @ reedyboy . net', 00878 ' Yuri Astrakhan <Firstname><Lastname>@gmail.com (creator, lead developer Sep 2006-Sep 2007)', 00879 '', 00880 'Please send your comments, suggestions and questions to [email protected]', 00881 'or file a bug report at https://bugzilla.wikimedia.org/' 00882 ); 00883 } 00884 00890 public function setHelp( $help = true ) { 00891 $this->mPrinter->setHelp( $help ); 00892 } 00893 00899 public function makeHelpMsg() { 00900 global $wgMemc, $wgAPICacheHelpTimeout; 00901 $this->setHelp(); 00902 // Get help text from cache if present 00903 $key = wfMemcKey( 'apihelp', $this->getModuleName(), 00904 SpecialVersion::getVersion( 'nodb' ) . 00905 $this->getShowVersions() ); 00906 if ( $wgAPICacheHelpTimeout > 0 ) { 00907 $cached = $wgMemc->get( $key ); 00908 if ( $cached ) { 00909 return $cached; 00910 } 00911 } 00912 $retval = $this->reallyMakeHelpMsg(); 00913 if ( $wgAPICacheHelpTimeout > 0 ) { 00914 $wgMemc->set( $key, $retval, $wgAPICacheHelpTimeout ); 00915 } 00916 return $retval; 00917 } 00918 00922 public function reallyMakeHelpMsg() { 00923 $this->setHelp(); 00924 00925 // Use parent to make default message for the main module 00926 $msg = parent::makeHelpMsg(); 00927 00928 $astriks = str_repeat( '*** ', 14 ); 00929 $msg .= "\n\n$astriks Modules $astriks\n\n"; 00930 foreach ( array_keys( $this->mModules ) as $moduleName ) { 00931 $module = new $this->mModules[$moduleName] ( $this, $moduleName ); 00932 $msg .= self::makeHelpMsgHeader( $module, 'action' ); 00933 $msg2 = $module->makeHelpMsg(); 00934 if ( $msg2 !== false ) { 00935 $msg .= $msg2; 00936 } 00937 $msg .= "\n"; 00938 } 00939 00940 $msg .= "\n$astriks Permissions $astriks\n\n"; 00941 foreach ( self::$mRights as $right => $rightMsg ) { 00942 $groups = User::getGroupsWithPermission( $right ); 00943 $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs( $rightMsg[ 'msg' ], $rightMsg[ 'params' ] ) . 00944 "\nGranted to:\n " . str_replace( '*', 'all', implode( ', ', $groups ) ) . "\n\n"; 00945 00946 } 00947 00948 $msg .= "\n$astriks Formats $astriks\n\n"; 00949 foreach ( array_keys( $this->mFormats ) as $formatName ) { 00950 $module = $this->createPrinterByName( $formatName ); 00951 $msg .= self::makeHelpMsgHeader( $module, 'format' ); 00952 $msg2 = $module->makeHelpMsg(); 00953 if ( $msg2 !== false ) { 00954 $msg .= $msg2; 00955 } 00956 $msg .= "\n"; 00957 } 00958 00959 $msg .= "\n*** Credits: ***\n " . implode( "\n ", $this->getCredits() ) . "\n"; 00960 00961 return $msg; 00962 } 00963 00969 public static function makeHelpMsgHeader( $module, $paramName ) { 00970 $modulePrefix = $module->getModulePrefix(); 00971 if ( strval( $modulePrefix ) !== '' ) { 00972 $modulePrefix = "($modulePrefix) "; 00973 } 00974 00975 return "* $paramName={$module->getModuleName()} $modulePrefix*"; 00976 } 00977 00978 private $mCanApiHighLimits = null; 00979 00984 public function canApiHighLimits() { 00985 if ( !isset( $this->mCanApiHighLimits ) ) { 00986 $this->mCanApiHighLimits = $this->getUser()->isAllowed( 'apihighlimits' ); 00987 } 00988 00989 return $this->mCanApiHighLimits; 00990 } 00991 00996 public function getShowVersions() { 00997 return $this->mShowVersions; 00998 } 00999 01006 public function getVersion() { 01007 $vers = array(); 01008 $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n https://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/"; 01009 $vers[] = __CLASS__ . ': $Id$'; 01010 $vers[] = ApiBase::getBaseVersion(); 01011 $vers[] = ApiFormatBase::getBaseVersion(); 01012 $vers[] = ApiQueryBase::getBaseVersion(); 01013 return $vers; 01014 } 01015 01024 protected function addModule( $mdlName, $mdlClass ) { 01025 $this->mModules[$mdlName] = $mdlClass; 01026 } 01027 01035 protected function addFormat( $fmtName, $fmtClass ) { 01036 $this->mFormats[$fmtName] = $fmtClass; 01037 } 01038 01043 function getModules() { 01044 return $this->mModules; 01045 } 01046 01053 public function getFormats() { 01054 return $this->mFormats; 01055 } 01056 } 01057 01064 class UsageException extends Exception { 01065 01066 private $mCodestr; 01067 private $mExtraData; 01068 01069 public function __construct( $message, $codestr, $code = 0, $extradata = null ) { 01070 parent::__construct( $message, $code ); 01071 $this->mCodestr = $codestr; 01072 $this->mExtraData = $extradata; 01073 } 01074 01078 public function getCodeString() { 01079 return $this->mCodestr; 01080 } 01081 01085 public function getMessageArray() { 01086 $result = array( 01087 'code' => $this->mCodestr, 01088 'info' => $this->getMessage() 01089 ); 01090 if ( is_array( $this->mExtraData ) ) { 01091 $result = array_merge( $result, $this->mExtraData ); 01092 } 01093 return $result; 01094 } 01095 01099 public function __toString() { 01100 return "{$this->getCodeString()}: {$this->getMessage()}"; 01101 } 01102 }