X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fapi%2FApiMain.php;h=cf06e5a567e24358e8424ee1ec299041e0a3c11d;hb=3441def388ddb150d6c88822bf9b0df8fac800b1;hp=712037fd6c420c1edb5977b5f47ea159a37834fb;hpb=7478ad3576559e28db3450bef93b638f5978468e;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index 712037fd6c..cf06e5a567 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -1,11 +1,10 @@ @gmail.com + * Created on Sep 4, 2006 + * + * Copyright © 2006 Yuri Astrakhan @gmail.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,19 +18,18 @@ * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * http://www.gnu.org/copyleft/gpl.html + * + * @file + * @defgroup API API */ if ( !defined( 'MEDIAWIKI' ) ) { // Eclipse helper - will be ignored in production - require_once ( 'ApiBase.php' ); + require_once( 'ApiBase.php' ); } -/** - * @defgroup API API - */ - /** * This is the main API class, used for both external and internal processing. * When executed, it will create the requested formatter object, @@ -55,7 +53,7 @@ class ApiMain extends ApiBase { /** * List of available modules: action name => module class */ - private static $Modules = array ( + private static $Modules = array( 'login' => 'ApiLogin', 'logout' => 'ApiLogout', 'query' => 'ApiQuery', @@ -65,6 +63,8 @@ class ApiMain extends ApiBase { 'feedwatchlist' => 'ApiFeedWatchlist', 'help' => 'ApiHelp', 'paraminfo' => 'ApiParamInfo', + 'rsd' => 'ApiRsd', + 'compare' => 'ApiComparePages', // Write modules 'purge' => 'ApiPurge', @@ -77,6 +77,7 @@ class ApiMain extends ApiBase { 'move' => 'ApiMove', 'edit' => 'ApiEditPage', 'upload' => 'ApiUpload', + 'filerevert' => 'ApiFileRevert', 'emailuser' => 'ApiEmailUser', 'watch' => 'ApiWatch', 'patrol' => 'ApiPatrol', @@ -87,7 +88,7 @@ class ApiMain extends ApiBase { /** * List of available formats: format name => format class */ - private static $Formats = array ( + private static $Formats = array( 'json' => 'ApiFormatJson', 'jsonfm' => 'ApiFormatJson', 'php' => 'ApiFormatPhp', @@ -102,7 +103,9 @@ class ApiMain extends ApiBase { 'txt' => 'ApiFormatTxt', 'txtfm' => 'ApiFormatTxt', 'dbg' => 'ApiFormatDbg', - 'dbgfm' => 'ApiFormatDbg' + 'dbgfm' => 'ApiFormatDbg', + 'dump' => 'ApiFormatDump', + 'dumpfm' => 'ApiFormatDump', ); /** @@ -111,38 +114,42 @@ class ApiMain extends ApiBase { * 'params' => array ( $someVarToSubst ) ), * ); */ - private static $mRights = array( 'writeapi' => array( - 'msg' => 'Use of the write API', - 'params' => array() - ), - 'apihighlimits' => array( - '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.', - 'params' => array ( ApiMain::LIMIT_SML2, ApiMain::LIMIT_BIG2 ) - ) + private static $mRights = array( + 'writeapi' => array( + 'msg' => 'Use of the write API', + 'params' => array() + ), + 'apihighlimits' => array( + '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.', + 'params' => array( ApiBase::LIMIT_SML2, ApiBase::LIMIT_BIG2 ) + ) ); + /** + * @var ApiFormatBase + */ + private $mPrinter; - private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames; + private $mModules, $mModuleNames, $mFormats, $mFormatNames; private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest; private $mInternalMode, $mSquidMaxage, $mModule; - private $mCacheControl = array( 'must-revalidate' => true ); + private $mCacheMode = 'private'; + private $mCacheControl = array(); /** - * Constructs an instance of ApiMain that utilizes the module and format specified by $request. - * - * @param $request object - if this is an instance of FauxRequest, errors are thrown and no printing occurs - * @param $enableWrite bool should be set to true if the api may modify data - */ + * Constructs an instance of ApiMain that utilizes the module and format specified by $request. + * + * @param $request WebRequest - if this is an instance of FauxRequest, errors are thrown and no printing occurs + * @param $enableWrite bool should be set to true if the api may modify data + */ public function __construct( $request, $enableWrite = false ) { - $this->mInternalMode = ( $request instanceof FauxRequest ); // Special handling for the main module: $parent === $this - parent :: __construct( $this, $this->mInternalMode ? 'main_int' : 'main' ); + parent::__construct( $this, $this->mInternalMode ? 'main_int' : 'main' ); if ( !$this->mInternalMode ) { - // Impose module restrictions. // If the current user cannot read, // Remove all modules other than login @@ -157,17 +164,17 @@ class ApiMain extends ApiBase { } global $wgAPIModules; // extension modules - $this->mModules = $wgAPIModules + self :: $Modules; + $this->mModules = $wgAPIModules + self::$Modules; $this->mModuleNames = array_keys( $this->mModules ); - $this->mFormats = self :: $Formats; + $this->mFormats = self::$Formats; $this->mFormatNames = array_keys( $this->mFormats ); $this->mResult = new ApiResult( $this ); $this->mShowVersions = false; $this->mEnableWrite = $enableWrite; - $this->mRequest = & $request; + $this->mRequest = &$request; $this->mSquidMaxage = - 1; // flag for executeActionWithErrorHandling() $this->mCommit = false; @@ -175,6 +182,7 @@ class ApiMain extends ApiBase { /** * Return true if the API was started by other PHP code using FauxRequest + * @return bool */ public function isInternalMode() { return $this->mInternalMode; @@ -182,6 +190,7 @@ class ApiMain extends ApiBase { /** * Return the request object that contains client's request + * @return WebRequest */ public function getRequest() { return $this->mRequest; @@ -189,6 +198,8 @@ class ApiMain extends ApiBase { /** * Get the ApiResult object associated with current request + * + * @return ApiResult */ public function getResult() { return $this->mResult; @@ -196,24 +207,26 @@ class ApiMain extends ApiBase { /** * Get the API module object. Only works after executeAction() + * + * @return ApiBase */ public function getModule() { return $this->mModule; } /** - * Only kept for backwards compatibility - * @deprecated Use isWriteMode() instead + * Get the result formatter object. Only works after setupExecuteAction() + * + * @return ApiFormatBase */ - public function requestWriteMode() { - if ( !$this->mEnableWrite ) - $this->dieUsageMsg( array( 'writedisabled' ) ); - if ( wfReadOnly() ) - $this->dieUsageMsg( array( 'readonlytext' ) ); + public function getPrinter() { + return $this->mPrinter; } /** * Set how long the response should be cached. + * + * @param $maxage */ public function setCacheMaxAge( $maxage ) { $this->setCacheControl( array( @@ -222,21 +235,96 @@ class ApiMain extends ApiBase { ) ); } + /** + * Set the type of caching headers which will be sent. + * + * @param $mode String One of: + * - 'public': Cache this object in public caches, if the maxage or smaxage + * parameter is set, or if setCacheMaxAge() was called. If a maximum age is + * not provided by any of these means, the object will be private. + * - 'private': Cache this object only in private client-side caches. + * - 'anon-public-user-private': Make this object cacheable for logged-out + * users, but private for logged-in users. IMPORTANT: If this is set, it must be + * set consistently for a given URL, it cannot be set differently depending on + * things like the contents of the database, or whether the user is logged in. + * + * If the wiki does not allow anonymous users to read it, the mode set here + * will be ignored, and private caching headers will always be sent. In other words, + * the "public" mode is equivalent to saying that the data sent is as public as a page + * view. + * + * For user-dependent data, the private mode should generally be used. The + * anon-public-user-private mode should only be used where there is a particularly + * good performance reason for caching the anonymous response, but where the + * response to logged-in users may differ, or may contain private data. + * + * If this function is never called, then the default will be the private mode. + */ + public function setCacheMode( $mode ) { + if ( !in_array( $mode, array( 'private', 'public', 'anon-public-user-private' ) ) ) { + wfDebug( __METHOD__ . ": unrecognised cache mode \"$mode\"\n" ); + // Ignore for forwards-compatibility + return; + } + + if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { + // Private wiki, only private headers + if ( $mode !== 'private' ) { + wfDebug( __METHOD__ . ": ignoring request for $mode cache mode, private wiki\n" ); + return; + } + } + + wfDebug( __METHOD__ . ": setting cache mode $mode\n" ); + $this->mCacheMode = $mode; + } + + /** + * @deprecated since 1.17 Private caching is now the default, so there is usually no + * need to call this function. If there is a need, you can use + * $this->setCacheMode('private') + */ + public function setCachePrivate() { + $this->setCacheMode( 'private' ); + } + /** * Set directives (key/value pairs) for the Cache-Control header. * Boolean values will be formatted as such, by including or omitting * without an equals sign. + * + * Cache control values set here will only be used if the cache mode is not + * private, see setCacheMode(). */ public function setCacheControl( $directives ) { $this->mCacheControl = $directives + $this->mCacheControl; } + /** + * Make sure Vary: Cookie and friends are set. Use this when the output of a request + * may be cached for anons but may not be cached for logged-in users. + * + * WARNING: This function must be called CONSISTENTLY for a given URL. This means that a + * given URL must either always or never call this function; if it sometimes does and + * sometimes doesn't, stuff will break. + * + * @deprecated since 1.17 Use setCacheMode( 'anon-public-user-private' ) + */ + public function setVaryCookie() { + $this->setCacheMode( 'anon-public-user-private' ); + } + /** * Create an instance of an output formatter by its name + * + * @param $format string + * + * @return ApiFormatBase */ public function createPrinterByName( $format ) { - if ( !isset( $this->mFormats[$format] ) ) + if ( !isset( $this->mFormats[$format] ) ) { $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' ); + } return new $this->mFormats[$format] ( $this, $format ); } @@ -245,10 +333,11 @@ class ApiMain extends ApiBase { */ public function execute() { $this->profileIn(); - if ( $this->mInternalMode ) + if ( $this->mInternalMode ) { $this->executeAction(); - else + } else { $this->executeActionWithErrorHandling(); + } $this->profileOut(); } @@ -258,7 +347,6 @@ class ApiMain extends ApiBase { * have been accumulated, and replace it with an error message and a help screen. */ protected function executeActionWithErrorHandling() { - // In case an error occurs during data output, // clear the output buffer and print just the error information ob_start(); @@ -280,13 +368,14 @@ class ApiMain extends ApiBase { $errCode = $this->substituteResultWithError( $e ); // Error results should not be cached - $this->setCacheMaxAge( 0 ); + $this->setCacheMode( 'private' ); $headerStr = 'MediaWiki-API-Error: ' . $errCode; - if ( $e->getCode() === 0 ) + if ( $e->getCode() === 0 ) { header( $headerStr ); - else + } else { header( $headerStr, true, $e->getCode() ); + } // Reset and print just the error message ob_clean(); @@ -296,6 +385,42 @@ class ApiMain extends ApiBase { $this->printResult( true ); } + // Send cache headers after any code which might generate an error, to + // avoid sending public cache headers for errors. + $this->sendCacheHeaders(); + + if ( $this->mPrinter->getIsHtml() && !$this->mPrinter->isDisabled() ) { + echo wfReportTime(); + } + + ob_end_flush(); + } + + protected function sendCacheHeaders() { + if ( $this->mCacheMode == 'private' ) { + header( 'Cache-Control: private' ); + return; + } + + if ( $this->mCacheMode == 'anon-public-user-private' ) { + global $wgUseXVO, $wgOut; + header( 'Vary: Accept-Encoding, Cookie' ); + if ( $wgUseXVO ) { + header( $wgOut->getXVO() ); + if ( $wgOut->haveCacheVaryCookies() ) { + // Logged in, mark this request private + header( 'Cache-Control: private' ); + return; + } + // Logged out, send normal public headers below + } elseif ( session_id() != '' ) { + // Logged in or otherwise has session (e.g. anonymous users who have edited) + // Mark request private + header( 'Cache-Control: private' ); + return; + } // else no XVO and anonymous, send public headers below + } + // If nobody called setCacheMaxAge(), use the (s)maxage parameters if ( !isset( $this->mCacheControl['s-maxage'] ) ) { $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' ); @@ -304,11 +429,20 @@ class ApiMain extends ApiBase { $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' ); } - // Set the cache expiration at the last moment, as any errors may change the expiration. - // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch - $exp = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] ); - $expires = ( $exp == 0 ? 1 : time() + $exp ); - header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expires ) ); + if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) { + // Public cache not requested + // Sending a Vary header in this case is harmless, and protects us + // against conditional calls of setCacheMaxAge(). + header( 'Cache-Control: private' ); + return; + } + + $this->mCacheControl['public'] = true; + + // Send an Expires header + $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] ); + $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge ); + header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expiryUnixTime ) ); // Construct the Cache-Control header $ccHeader = ''; @@ -324,111 +458,130 @@ class ApiMain extends ApiBase { $separator = ', '; } } - - header( "Cache-Control: $ccHeader" ); - - if ( $this->mPrinter->getIsHtml() ) - echo wfReportTime(); - ob_end_flush(); + header( "Cache-Control: $ccHeader" ); } /** * Replace the result data with the information about an exception. * Returns the error code + * @param $e Exception + * @return string */ protected function substituteResultWithError( $e ) { - // Printer may not be initialized if the extractRequestParams() fails for the main module if ( !isset ( $this->mPrinter ) ) { // The printer has not been created yet. Try to manually get formatter value. $value = $this->getRequest()->getVal( 'format', self::API_DEFAULT_FORMAT ); - if ( !in_array( $value, $this->mFormatNames ) ) + if ( !in_array( $value, $this->mFormatNames ) ) { $value = self::API_DEFAULT_FORMAT; + } $this->mPrinter = $this->createPrinterByName( $value ); - if ( $this->mPrinter->getNeedsRawData() ) + if ( $this->mPrinter->getNeedsRawData() ) { $this->getResult()->setRawMode(); + } } if ( $e instanceof UsageException ) { - // // User entered incorrect parameters - print usage screen - // $errMessage = $e->getMessageArray(); // Only print the help message when this is for the developer, not runtime - if ( $this->mPrinter->getWantsHelp() || $this->mAction == 'help' ) - ApiResult :: setContent( $errMessage, $this->makeHelpMsg() ); + if ( $this->mPrinter->getWantsHelp() || $this->mAction == 'help' ) { + ApiResult::setContent( $errMessage, $this->makeHelpMsg() ); + } } else { global $wgShowSQLErrors, $wgShowExceptionDetails; - // // Something is seriously wrong - // if ( ( $e instanceof DBQueryError ) && !$wgShowSQLErrors ) { - $info = "Database query error"; + $info = 'Database query error'; } else { $info = "Exception Caught: {$e->getMessage()}"; } - $errMessage = array ( + $errMessage = array( 'code' => 'internal_api_error_' . get_class( $e ), 'info' => $info, ); - ApiResult :: setContent( $errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : "" ); + ApiResult::setContent( $errMessage, $wgShowExceptionDetails ? "\n\n{$e->getTraceAsString()}\n\n" : '' ); } $this->getResult()->reset(); $this->getResult()->disableSizeCheck(); // Re-add the id $requestid = $this->getParameter( 'requestid' ); - if ( !is_null( $requestid ) ) + if ( !is_null( $requestid ) ) { $this->getResult()->addValue( null, 'requestid', $requestid ); + } + // servedby is especially useful when debugging errors + $this->getResult()->addValue( null, 'servedby', wfHostName() ); $this->getResult()->addValue( null, 'error', $errMessage ); return $errMessage['code']; } /** - * Execute the actual module, without any error handling + * Set up for the execution. + * @return array */ - protected function executeAction() { + protected function setupExecuteAction() { // First add the id to the top element $requestid = $this->getParameter( 'requestid' ); - if ( !is_null( $requestid ) ) + if ( !is_null( $requestid ) ) { $this->getResult()->addValue( null, 'requestid', $requestid ); + } + $servedby = $this->getParameter( 'servedby' ); + if ( $servedby ) { + $this->getResult()->addValue( null, 'servedby', wfHostName() ); + } $params = $this->extractRequestParams(); - + $this->mShowVersions = $params['version']; $this->mAction = $params['action']; if ( !is_string( $this->mAction ) ) { - $this->dieUsage( "The API requires a valid action parameter", 'unknown_action' ); + $this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' ); } - + + return $params; + } + + /** + * Set up the module for response + * @return ApiBase The module that will handle this action + */ + protected function setupModule() { // Instantiate the module requested by the user $module = new $this->mModules[$this->mAction] ( $this, $this->mAction ); $this->mModule = $module; $moduleParams = $module->extractRequestParams(); - - //Die if token required, but not provided (unless there is a gettoken parameter) + + // Die if token required, but not provided (unless there is a gettoken parameter) $salt = $module->getTokenSalt(); - if ( $salt != false ) - { - if ( !isset( $moduleParams['token'] ) && !isset( $moduleParams['gettoken'] ) ) { + if ( $salt !== false && !isset( $moduleParams['gettoken'] ) ) { + if ( !isset( $moduleParams['token'] ) ) { $this->dieUsageMsg( array( 'missingparam', 'token' ) ); } else { global $wgUser; - if ( ( $salt != null /*&& !$wgUser->matchEditToken( $moduleParams['token'], $salt )*/ ) - /*|| !$wgUser->matchEditToken( $moduleParams['token'] )*/ ) { + if ( !$wgUser->matchEditToken( $moduleParams['token'], $salt, $this->getMain()->getRequest() ) ) { $this->dieUsageMsg( array( 'sessionfailure' ) ); } } } + return $module; + } + /** + * Check the max lag if necessary + * @param $module ApiBase object: Api module being used + * @param $params Array an array containing the request parameters. + * @return boolean True on success, false should exit immediately + */ + protected function checkMaxLag( $module, $params ) { if ( $module->shouldCheckMaxlag() && isset( $params['maxlag'] ) ) { // Check for maxlag global $wgShowHostnames; @@ -442,36 +595,75 @@ class ApiMain extends ApiBase { } else { $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' ); } - return; + return false; } } + return true; + } - global $wgUser, $wgGroupPermissions; - if ( $module->isReadMode() && !$wgGroupPermissions['*']['read'] && !$wgUser->isAllowed( 'read' ) ) + + /** + * Check for sufficient permissions to execute + * @param $module ApiBase An Api module + */ + protected function checkExecutePermissions( $module ) { + global $wgUser; + if ( $module->isReadMode() && !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) && + !$wgUser->isAllowed( 'read' ) ) + { $this->dieUsageMsg( array( 'readrequired' ) ); + } if ( $module->isWriteMode() ) { - if ( !$this->mEnableWrite ) + if ( !$this->mEnableWrite ) { $this->dieUsageMsg( array( 'writedisabled' ) ); - if ( !$wgUser->isAllowed( 'writeapi' ) ) + } + if ( !$wgUser->isAllowed( 'writeapi' ) ) { $this->dieUsageMsg( array( 'writerequired' ) ); - if ( wfReadOnly() ) + } + if ( wfReadOnly() ) { $this->dieReadOnly(); + } } + } - if ( !$this->mInternalMode ) { - // Ignore mustBePosted() for internal calls - if ( $module->mustBePosted() && !$this->mRequest->wasPosted() ) - $this->dieUsageMsg( array ( 'mustbeposted', $this->mAction ) ); - - // See if custom printer is used - $this->mPrinter = $module->getCustomPrinter(); - if ( is_null( $this->mPrinter ) ) { - // Create an appropriate printer - $this->mPrinter = $this->createPrinterByName( $params['format'] ); - } + /** + * Check POST for external response and setup result printer + * @param $module ApiBase An Api module + * @param $params Array an array with the request parameters + */ + protected function setupExternalResponse( $module, $params ) { + // Ignore mustBePosted() for internal calls + if ( $module->mustBePosted() && !$this->mRequest->wasPosted() ) { + $this->dieUsageMsg( array( 'mustbeposted', $this->mAction ) ); + } - if ( $this->mPrinter->getNeedsRawData() ) - $this->getResult()->setRawMode(); + // See if custom printer is used + $this->mPrinter = $module->getCustomPrinter(); + if ( is_null( $this->mPrinter ) ) { + // Create an appropriate printer + $this->mPrinter = $this->createPrinterByName( $params['format'] ); + } + + if ( $this->mPrinter->getNeedsRawData() ) { + $this->getResult()->setRawMode(); + } + } + + /** + * Execute the actual module, without any error handling + */ + protected function executeAction() { + $params = $this->setupExecuteAction(); + $module = $this->setupModule(); + + $this->checkExecutePermissions( $module ); + + if ( !$this->checkMaxLag( $module, $params ) ) { + return; + } + + if ( !$this->mInternalMode ) { + $this->setupExternalResponse( $module, $params ); } // Execute @@ -488,16 +680,20 @@ class ApiMain extends ApiBase { /** * Print results using the current printer + * + * @param $isError bool */ protected function printResult( $isError ) { $this->getResult()->cleanUpUTF8(); $printer = $this->mPrinter; $printer->profileIn(); - /* If the help message is requested in the default (xmlfm) format, + /** + * If the help message is requested in the default (xmlfm) format, * tell the printer not to escape ampersands so that our links do - * not break. */ - $printer->setUnescapeAmps ( ( $this->mAction == 'help' || $isError ) + * not break. + */ + $printer->setUnescapeAmps( ( $this->mAction == 'help' || $isError ) && $printer->getFormat() == 'XML' && $printer->getIsHtml() ); $printer->initPrinter( $isError ); @@ -507,77 +703,89 @@ class ApiMain extends ApiBase { $printer->profileOut(); } + /** + * @return bool + */ public function isReadMode() { return false; } /** * See ApiBase for description. + * + * @return array */ public function getAllowedParams() { - return array ( - 'format' => array ( - ApiBase :: PARAM_DFLT => ApiMain :: API_DEFAULT_FORMAT, - ApiBase :: PARAM_TYPE => $this->mFormatNames + return array( + 'format' => array( + ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT, + ApiBase::PARAM_TYPE => $this->mFormatNames ), - 'action' => array ( - ApiBase :: PARAM_DFLT => 'help', - ApiBase :: PARAM_TYPE => $this->mModuleNames + 'action' => array( + ApiBase::PARAM_DFLT => 'help', + ApiBase::PARAM_TYPE => $this->mModuleNames ), 'version' => false, - 'maxlag' => array ( - ApiBase :: PARAM_TYPE => 'integer' + 'maxlag' => array( + ApiBase::PARAM_TYPE => 'integer' ), - 'smaxage' => array ( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => 0 + 'smaxage' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => 0 ), - 'maxage' => array ( - ApiBase :: PARAM_TYPE => 'integer', - ApiBase :: PARAM_DFLT => 0 + 'maxage' => array( + ApiBase::PARAM_TYPE => 'integer', + ApiBase::PARAM_DFLT => 0 ), 'requestid' => null, + 'servedby' => false, ); } /** * See ApiBase for description. + * + * @return array */ public function getParamDescription() { - return array ( + return array( 'format' => 'The format of the output', - 'action' => 'What action you would like to perform', + 'action' => 'What action you would like to perform. See below for module help', 'version' => 'When showing help, include version for each module', 'maxlag' => 'Maximum lag', 'smaxage' => 'Set the s-maxage header to this many seconds. Errors are never cached', 'maxage' => 'Set the max-age header to this many seconds. Errors are never cached', 'requestid' => 'Request ID to distinguish requests. This will just be output back to you', + 'servedby' => 'Include the hostname that served the request in the results. Unconditionally shown on error', ); } /** * See ApiBase for description. + * + * @return array */ public function getDescription() { - return array ( + return array( '', '', - '******************************************************************', - '** **', - '** This is an auto-generated MediaWiki API documentation page **', - '** **', - '** Documentation and Examples: **', - '** http://www.mediawiki.org/wiki/API **', - '** **', - '******************************************************************', + '**********************************************************************************************************', + '** **', + '** This is an auto-generated MediaWiki API documentation page **', + '** **', + '** Documentation and Examples: **', + '** http://www.mediawiki.org/wiki/API **', + '** **', + '**********************************************************************************************************', '', - 'Status: All features shown on this page should be working, but the API', - ' is still in active development, and may change at any time.', - ' Make sure to monitor our mailing list for any updates.', + 'Status: All features shown on this page should be working, but the API', + ' is still in active development, and may change at any time.', + ' Make sure to monitor our mailing list for any updates', '', - 'Documentation: http://www.mediawiki.org/wiki/API', - 'Mailing list: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api', - 'Bugs & Requests: http://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts', + 'Documentation: http://www.mediawiki.org/wiki/API', + 'Mailing list: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api', + 'Api Announcements: http://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce', + 'Bugs & Requests: http://bugzilla.wikimedia.org/buglist.cgi?component=API&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&order=bugs.delta_ts', '', '', '', @@ -585,24 +793,28 @@ class ApiMain extends ApiBase { '', ); } - - public function getPossibleErrors() { + + /** + * @return array + */ + public function getPossibleErrors() { return array_merge( parent::getPossibleErrors(), array( array( 'readonlytext' ), array( 'code' => 'unknown_format', 'info' => 'Unrecognized format: format' ), array( 'code' => 'unknown_action', 'info' => 'The API requires a valid action parameter' ), array( 'code' => 'maxlag', 'info' => 'Waiting for host: x seconds lagged' ), array( 'code' => 'maxlag', 'info' => 'Waiting for a database server: x seconds lagged' ), - ) ); + ) ); } /** * Returns an array of strings with credits for the API + * @return array */ protected function getCredits() { return array( 'API developers:', - ' Roan Kattouw .@home.nl (lead developer Sep 2007-present)', + ' Roan Kattouw .@gmail.com (lead developer Sep 2007-present)', ' Victor Vasiliev - vasilvv at gee mail dot com', ' Bryan Tong Minh - bryan . tongminh @ gmail . com', ' Sam Reed - sam @ reedyboy . net', @@ -613,107 +825,101 @@ class ApiMain extends ApiBase { ); } + /** + * Sets whether the pretty-printer should format *bold* and $italics$ + * + * @param $help bool + */ + public function setHelp( $help = true ) { + $this->mPrinter->setHelp( $help ); + } + /** * Override the parent to generate help messages for all available modules. + * + * @return string */ public function makeHelpMsg() { - global $wgMemc, $wgAPICacheHelp, $wgAPICacheHelpTimeout; - $this->mPrinter->setHelp(); + global $wgMemc, $wgAPICacheHelpTimeout; + $this->setHelp(); // Get help text from cache if present $key = wfMemcKey( 'apihelp', $this->getModuleName(), SpecialVersion::getVersion( 'nodb' ) . $this->getMain()->getShowVersions() ); - if ( $wgAPICacheHelp ) { + if ( $wgAPICacheHelpTimeout > 0 ) { $cached = $wgMemc->get( $key ); - if ( $cached ) + if ( $cached ) { return $cached; + } } $retval = $this->reallyMakeHelpMsg(); - if ( $wgAPICacheHelp ) + if ( $wgAPICacheHelpTimeout > 0 ) { $wgMemc->set( $key, $retval, $wgAPICacheHelpTimeout ); + } return $retval; } + /** + * @return mixed|string + */ public function reallyMakeHelpMsg() { - - $this->mPrinter->setHelp(); + $this->setHelp(); // Use parent to make default message for the main module - $msg = parent :: makeHelpMsg(); + $msg = parent::makeHelpMsg(); - $astriks = str_repeat( '*** ', 10 ); + $astriks = str_repeat( '*** ', 14 ); $msg .= "\n\n$astriks Modules $astriks\n\n"; - foreach ( $this->mModules as $moduleName => $unused ) { + foreach ( array_keys( $this->mModules ) as $moduleName ) { $module = new $this->mModules[$moduleName] ( $this, $moduleName ); $msg .= self::makeHelpMsgHeader( $module, 'action' ); $msg2 = $module->makeHelpMsg(); - if ( $msg2 !== false ) + if ( $msg2 !== false ) { $msg .= $msg2; + } $msg .= "\n"; } $msg .= "\n$astriks Permissions $astriks\n\n"; - foreach ( self :: $mRights as $right => $rightMsg ) { + foreach ( self::$mRights as $right => $rightMsg ) { $groups = User::getGroupsWithPermission( $right ); $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs( $rightMsg[ 'msg' ], $rightMsg[ 'params' ] ) . - "\nGranted to:\n " . str_replace( "*", "all", implode( ", ", $groups ) ) . "\n"; + "\nGranted to:\n " . str_replace( '*', 'all', implode( ', ', $groups ) ) . "\n\n"; } $msg .= "\n$astriks Formats $astriks\n\n"; - foreach ( $this->mFormats as $formatName => $unused ) { + foreach ( array_keys( $this->mFormats ) as $formatName ) { $module = $this->createPrinterByName( $formatName ); $msg .= self::makeHelpMsgHeader( $module, 'format' ); $msg2 = $module->makeHelpMsg(); - if ( $msg2 !== false ) + if ( $msg2 !== false ) { $msg .= $msg2; + } $msg .= "\n"; } $msg .= "\n*** Credits: ***\n " . implode( "\n ", $this->getCredits() ) . "\n"; - return $msg; } + /** + * @param $module ApiBase + * @param $paramName String What type of request is this? e.g. action, query, list, prop, meta, format + * @return string + */ public static function makeHelpMsgHeader( $module, $paramName ) { $modulePrefix = $module->getModulePrefix(); - if ( strval( $modulePrefix ) !== '' ) + if ( strval( $modulePrefix ) !== '' ) { $modulePrefix = "($modulePrefix) "; + } return "* $paramName={$module->getModuleName()} $modulePrefix*"; } - private $mIsBot = null; - private $mIsSysop = null; private $mCanApiHighLimits = null; - /** - * Returns true if the currently logged in user is a bot, false otherwise - * OBSOLETE, use canApiHighLimits() instead - */ - public function isBot() { - if ( !isset ( $this->mIsBot ) ) { - global $wgUser; - $this->mIsBot = $wgUser->isAllowed( 'bot' ); - } - return $this->mIsBot; - } - - /** - * Similar to isBot(), this method returns true if the logged in user is - * a sysop, and false if not. - * OBSOLETE, use canApiHighLimits() instead - */ - public function isSysop() { - if ( !isset ( $this->mIsSysop ) ) { - global $wgUser; - $this->mIsSysop = in_array( 'sysop', $wgUser->getGroups() ); - } - - return $this->mIsSysop; - } - /** * Check whether the current user is allowed to use high limits * @return bool @@ -738,14 +944,16 @@ class ApiMain extends ApiBase { /** * Returns the version information of this file, plus it includes * the versions for all files that are not callable proper API modules + * + * @return array */ public function getVersion() { - $vers = array (); + $vers = array(); $vers[] = 'MediaWiki: ' . SpecialVersion::getVersion() . "\n http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/"; $vers[] = __CLASS__ . ': $Id$'; - $vers[] = ApiBase :: getBaseVersion(); - $vers[] = ApiFormatBase :: getBaseVersion(); - $vers[] = ApiQueryBase :: getBaseVersion(); + $vers[] = ApiBase::getBaseVersion(); + $vers[] = ApiFormatBase::getBaseVersion(); + $vers[] = ApiQueryBase::getBaseVersion(); return $vers; } @@ -754,7 +962,6 @@ class ApiMain extends ApiBase { * classes who wish to add their own modules to their lexicon or override the * behavior of inherent ones. * - * @access protected * @param $mdlName String The identifier for this module. * @param $mdlClass String The class where this module is implemented. */ @@ -766,9 +973,8 @@ class ApiMain extends ApiBase { * Add or overwrite an output format for this ApiMain. Intended for use by extending * classes who wish to add to or modify current formatters. * - * @access protected - * @param $fmtName The identifier for this format. - * @param $fmtClass The class implementing this format. + * @param $fmtName string The identifier for this format. + * @param $fmtClass ApiFormatBase The class implementing this format. */ protected function addFormat( $fmtName, $fmtClass ) { $this->mFormats[$fmtName] = $fmtClass; @@ -776,10 +982,21 @@ class ApiMain extends ApiBase { /** * Get the array mapping module names to class names + * @return array */ function getModules() { return $this->mModules; } + + /** + * Returns the list of supported formats in form ( 'format' => 'ClassName' ) + * + * @since 1.18 + * @return array + */ + public function getFormats() { + return $this->mFormats; + } } /** @@ -794,22 +1011,26 @@ class UsageException extends Exception { private $mExtraData; public function __construct( $message, $codestr, $code = 0, $extradata = null ) { - parent :: __construct( $message, $code ); + parent::__construct( $message, $code ); $this->mCodestr = $codestr; $this->mExtraData = $extradata; } + public function getCodeString() { return $this->mCodestr; } + public function getMessageArray() { - $result = array ( - 'code' => $this->mCodestr, - 'info' => $this->getMessage() + $result = array( + 'code' => $this->mCodestr, + 'info' => $this->getMessage() ); - if ( is_array( $this->mExtraData ) ) + if ( is_array( $this->mExtraData ) ) { $result = array_merge( $result, $this->mExtraData ); + } return $result; } + public function __toString() { return "{$this->getCodeString()}: {$this->getMessage()}"; }