X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;ds=sidebyside;f=includes%2Fapi%2FApiMain.php;h=52f1d95830ffda43a43242e8251463b1a95bdc4c;hb=d08751030c97a60b066f70e6962d426367a67e8d;hp=a2da166a6c462839a8b0ce13f6a0912c5ec153c7;hpb=44f8496104912253ca38053d159e1f0de1c949f9;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index a2da166a6c..52f1d95830 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -46,6 +46,11 @@ class ApiMain extends ApiBase { */ const API_DEFAULT_FORMAT = 'jsonfm'; + /** + * When no uselang parameter is given, this language will be used + */ + const API_DEFAULT_USELANG = 'user'; + /** * List of available modules: action name => module class */ @@ -74,6 +79,7 @@ class ApiMain extends ApiBase { 'tokens' => 'ApiTokens', 'checktoken' => 'ApiCheckToken', 'cspreport' => 'ApiCSPReport', + 'validatepassword' => 'ApiValidatePassword', // Write modules 'purge' => 'ApiPurge', @@ -100,6 +106,7 @@ class ApiMain extends ApiBase { 'managetags' => 'ApiManageTags', 'tag' => 'ApiTag', 'mergehistory' => 'ApiMergeHistory', + 'setpagelanguage' => 'ApiSetPageLanguage', ]; /** @@ -140,7 +147,7 @@ class ApiMain extends ApiBase { */ private $mPrinter; - private $mModuleMgr, $mResult, $mErrorFormatter; + private $mModuleMgr, $mResult, $mErrorFormatter = null; /** @var ApiContinuationManager|null */ private $mContinuationManager; private $mAction; @@ -229,7 +236,11 @@ class ApiMain extends ApiBase { } } - $uselang = $this->getParameter( 'uselang' ); + $this->mResult = new ApiResult( $this->getConfig()->get( 'APIMaxResultSize' ) ); + + // Setup uselang. This doesn't use $this->getParameter() + // because we're not ready to handle errors yet. + $uselang = $request->getVal( 'uselang', self::API_DEFAULT_USELANG ); if ( $uselang === 'user' ) { // Assume the parent context is going to return the user language // for uselang=user (see T85635). @@ -247,6 +258,29 @@ class ApiMain extends ApiBase { } } + // Set up the error formatter. This doesn't use $this->getParameter() + // because we're not ready to handle errors yet. + $errorFormat = $request->getVal( 'errorformat', 'bc' ); + $errorLangCode = $request->getVal( 'errorlang', 'uselang' ); + $errorsUseDB = $request->getCheck( 'errorsuselocal' ); + if ( in_array( $errorFormat, [ 'plaintext', 'wikitext', 'html', 'raw', 'none' ], true ) ) { + if ( $errorLangCode === 'uselang' ) { + $errorLang = $this->getLanguage(); + } elseif ( $errorLangCode === 'content' ) { + global $wgContLang; + $errorLang = $wgContLang; + } else { + $errorLangCode = RequestContext::sanitizeLangCode( $errorLangCode ); + $errorLang = Language::factory( $errorLangCode ); + } + $this->mErrorFormatter = new ApiErrorFormatter( + $this->mResult, $errorLang, $errorFormat, $errorsUseDB + ); + } else { + $this->mErrorFormatter = new ApiErrorFormatter_BackCompat( $this->mResult ); + } + $this->mResult->setErrorFormatter( $this->getErrorFormatter() ); + $this->mModuleMgr = new ApiModuleManager( $this ); $this->mModuleMgr->addModules( self::$Modules, 'action' ); $this->mModuleMgr->addModules( $config->get( 'APIModules' ), 'action' ); @@ -255,9 +289,6 @@ class ApiMain extends ApiBase { Hooks::run( 'ApiMain::moduleManager', [ $this->mModuleMgr ] ); - $this->mResult = new ApiResult( $this->getConfig()->get( 'APIMaxResultSize' ) ); - $this->mErrorFormatter = new ApiErrorFormatter_BackCompat( $this->mResult ); - $this->mResult->setErrorFormatter( $this->mErrorFormatter ); $this->mContinuationManager = null; $this->mEnableWrite = $enableWrite; @@ -464,7 +495,9 @@ class ApiMain extends ApiBase { public function createPrinterByName( $format ) { $printer = $this->mModuleMgr->getModule( $format, 'format' ); if ( $printer === null ) { - $this->dieUsage( "Unrecognized format: {$format}", 'unknown_format' ); + $this->dieWithError( + [ 'apierror-unknownformat', wfEscapeWikiText( $format ) ], 'unknown_format' + ); } return $printer; @@ -542,7 +575,7 @@ class ApiMain extends ApiBase { */ protected function handleException( Exception $e ) { // Bug 63145: Rollback any open database transactions - if ( !( $e instanceof UsageException ) ) { + if ( !( $e instanceof ApiUsageException || $e instanceof UsageException ) ) { // UsageExceptions are intentional, so don't rollback if that's the case try { MWExceptionHandler::rollbackMasterChangesAndLog( $e ); @@ -557,7 +590,7 @@ class ApiMain extends ApiBase { Hooks::run( 'ApiMain::onException', [ $this, $e ] ); // Log it - if ( !( $e instanceof UsageException ) ) { + if ( !( $e instanceof ApiUsageException || $e instanceof UsageException ) ) { MWExceptionHandler::logException( $e ); } @@ -565,13 +598,13 @@ class ApiMain extends ApiBase { // If this fails, an unhandled exception should be thrown so that global error // handler will process and log it. - $errCode = $this->substituteResultWithError( $e ); + $errCodes = $this->substituteResultWithError( $e ); // Error results should not be cached $this->setCacheMode( 'private' ); $response = $this->getRequest()->response(); - $headerStr = 'MediaWiki-API-Error: ' . $errCode; + $headerStr = 'MediaWiki-API-Error: ' . join( ', ', $errCodes ); $response->header( $headerStr ); // Reset and print just the error message @@ -580,14 +613,31 @@ class ApiMain extends ApiBase { // Printer may not be initialized if the extractRequestParams() fails for the main module $this->createErrorPrinter(); + $failed = false; try { $this->printResult( $e->getCode() ); + } catch ( ApiUsageException $ex ) { + // The error printer itself is failing. Try suppressing its request + // parameters and redo. + $failed = true; + $this->addWarning( 'apiwarn-errorprinterfailed' ); + foreach ( $ex->getStatusValue()->getErrors() as $error ) { + try { + $this->mPrinter->addWarning( $error ); + } catch ( Exception $ex2 ) { + // WTF? + $this->addWarning( $error ); + } + } } catch ( UsageException $ex ) { // The error printer itself is failing. Try suppressing its request // parameters and redo. - $this->setWarning( - 'Error printer failed (will retry without params): ' . $ex->getMessage() + $failed = true; + $this->addWarning( + [ 'apiwarn-errorprinterfailed-ex', $ex->getMessage() ], 'errorprinterfailed' ); + } + if ( $failed ) { $this->mPrinter = null; $this->createErrorPrinter(); $this->mPrinter->forceDefaultParams(); @@ -635,8 +685,8 @@ class ApiMain extends ApiBase { * If the parameter and the header do match, the header is checked against $wgCrossSiteAJAXdomains * and $wgCrossSiteAJAXdomainExceptions, and if the origin qualifies, the appropriate CORS * headers are set. - * http://www.w3.org/TR/cors/#resource-requests - * http://www.w3.org/TR/cors/#resource-preflight-requests + * https://www.w3.org/TR/cors/#resource-requests + * https://www.w3.org/TR/cors/#resource-preflight-requests * * @return bool False if the caller should abort (403 case), true otherwise (all other cases) */ @@ -718,7 +768,7 @@ class ApiMain extends ApiBase { $response->header( "Access-Control-Allow-Origin: $allowOrigin" ); $response->header( "Access-Control-Allow-Credentials: $allowCredentials" ); - // http://www.w3.org/TR/resource-timing/#timing-allow-origin + // https://www.w3.org/TR/resource-timing/#timing-allow-origin if ( $allowTiming !== false ) { $response->header( "Timing-Allow-Origin: $allowTiming" ); } @@ -958,99 +1008,147 @@ class ApiMain extends ApiBase { /** * Create an error message for the given exception. * - * If the exception is a UsageException then - * UsageException::getMessageArray() will be called to create the message. + * If an ApiUsageException, errors/warnings will be extracted from the + * embedded StatusValue. + * + * If a base UsageException, the getMessageArray() method will be used to + * extract the code and English message for a single error (no warnings). + * + * Any other exception will be returned with a generic code and wrapper + * text around the exception's (presumably English) message as a single + * error (no warnings). * * @param Exception $e - * @return array ['code' => 'some string', 'info' => 'some other string'] + * @param string $type 'error' or 'warning' + * @return ApiMessage[] * @since 1.27 */ - protected function errorMessageFromException( $e ) { - if ( $e instanceof UsageException ) { + protected function errorMessagesFromException( $e, $type = 'error' ) { + $messages = []; + if ( $e instanceof ApiUsageException ) { + foreach ( $e->getStatusValue()->getErrorsByType( $type ) as $error ) { + $messages[] = ApiMessage::create( $error ); + } + } elseif ( $type !== 'error' ) { + // None of the rest have any messages for non-error types + } elseif ( $e instanceof UsageException ) { // User entered incorrect parameters - generate error response - $errMessage = $e->getMessageArray(); + $data = $e->getMessageArray(); + $code = $data['code']; + $info = $data['info']; + unset( $data['code'], $data['info'] ); + $messages[] = new ApiRawMessage( [ '$1', $info ], $code, $data ); } else { - $config = $this->getConfig(); // Something is seriously wrong + $config = $this->getConfig(); + $code = 'internal_api_error_' . get_class( $e ); if ( ( $e instanceof DBQueryError ) && !$config->get( 'ShowSQLErrors' ) ) { - $info = 'Database query error'; + $params = [ 'apierror-databaseerror', WebRequest::getRequestId() ]; } else { - $info = "Exception Caught: {$e->getMessage()}"; + $params = [ + 'apierror-exceptioncaught', + WebRequest::getRequestId(), + $e instanceof ILocalizedException + ? $e->getMessageObject() + : wfEscapeWikiText( $e->getMessage() ) + ]; } - - $errMessage = [ - 'code' => 'internal_api_error_' . get_class( $e ), - 'info' => '[' . WebRequest::getRequestId() . '] ' . $info, - ]; + $messages[] = ApiMessage::create( $params, $code ); } - return $errMessage; + return $messages; } /** * Replace the result data with the information about an exception. - * Returns the error code * @param Exception $e - * @return string + * @return string[] Error codes */ protected function substituteResultWithError( $e ) { $result = $this->getResult(); + $formatter = $this->getErrorFormatter(); $config = $this->getConfig(); + $errorCodes = []; - $errMessage = $this->errorMessageFromException( $e ); - if ( $e instanceof UsageException ) { - // User entered incorrect parameters - generate error response + // Remember existing warnings and errors across the reset + $errors = $result->getResultData( [ 'errors' ] ); + $warnings = $result->getResultData( [ 'warnings' ] ); + $result->reset(); + if ( $warnings !== null ) { + $result->addValue( null, 'warnings', $warnings, ApiResult::NO_SIZE_CHECK ); + } + if ( $errors !== null ) { + $result->addValue( null, 'errors', $errors, ApiResult::NO_SIZE_CHECK ); + + // Collect the copied error codes for the return value + foreach ( $errors as $error ) { + if ( isset( $error['code'] ) ) { + $errorCodes[$error['code']] = true; + } + } + } + + // Add errors from the exception + $modulePath = $e instanceof ApiUsageException ? $e->getModulePath() : null; + foreach ( $this->errorMessagesFromException( $e, 'error' ) as $msg ) { + $errorCodes[$msg->getApiCode()] = true; + $formatter->addError( $modulePath, $msg ); + } + foreach ( $this->errorMessagesFromException( $e, 'warning' ) as $msg ) { + $formatter->addWarning( $modulePath, $msg ); + } + + // Add additional data. Path depends on whether we're in BC mode or not. + // Data depends on the type of exception. + if ( $formatter instanceof ApiErrorFormatter_BackCompat ) { + $path = [ 'error' ]; + } else { + $path = null; + } + if ( $e instanceof ApiUsageException || $e instanceof UsageException ) { $link = wfExpandUrl( wfScript( 'api' ) ); - ApiResult::setContentValue( $errMessage, 'docref', "See $link for API usage" ); + $result->addContentValue( + $path, + 'docref', + $this->msg( 'api-usage-docref', $link )->inLanguage( $formatter->getLanguage() )->text() + ); } else { - // Something is seriously wrong if ( $config->get( 'ShowExceptionDetails' ) ) { - ApiResult::setContentValue( - $errMessage, + $result->addContentValue( + $path, 'trace', - MWExceptionHandler::getRedactedTraceAsString( $e ) + $this->msg( 'api-exception-trace', + get_class( $e ), + $e->getFile(), + $e->getLine(), + MWExceptionHandler::getRedactedTraceAsString( $e ) + )->inLanguage( $formatter->getLanguage() )->text() ); } } - // Remember all the warnings to re-add them later - $warnings = $result->getResultData( [ 'warnings' ] ); + // Add the id and such + $this->addRequestedFields( [ 'servedby' ] ); - $result->reset(); - // Re-add the id - $requestid = $this->getParameter( 'requestid' ); - if ( !is_null( $requestid ) ) { - $result->addValue( null, 'requestid', $requestid, ApiResult::NO_SIZE_CHECK ); - } - if ( $config->get( 'ShowHostnames' ) ) { - // servedby is especially useful when debugging errors - $result->addValue( null, 'servedby', wfHostname(), ApiResult::NO_SIZE_CHECK ); - } - if ( $warnings !== null ) { - $result->addValue( null, 'warnings', $warnings, ApiResult::NO_SIZE_CHECK ); - } - - $result->addValue( null, 'error', $errMessage, ApiResult::NO_SIZE_CHECK ); - - return $errMessage['code']; + return array_keys( $errorCodes ); } /** - * Set up for the execution. - * @return array + * Add requested fields to the result + * @param string[] $force Which fields to force even if not requested. Accepted values are: + * - servedby */ - protected function setupExecuteAction() { - // First add the id to the top element + protected function addRequestedFields( $force = [] ) { $result = $this->getResult(); + $requestid = $this->getParameter( 'requestid' ); - if ( !is_null( $requestid ) ) { - $result->addValue( null, 'requestid', $requestid ); + if ( $requestid !== null ) { + $result->addValue( null, 'requestid', $requestid, ApiResult::NO_SIZE_CHECK ); } - if ( $this->getConfig()->get( 'ShowHostnames' ) ) { - $servedby = $this->getParameter( 'servedby' ); - if ( $servedby ) { - $result->addValue( null, 'servedby', wfHostname() ); - } + if ( $this->getConfig()->get( 'ShowHostnames' ) && ( + in_array( 'servedby', $force, true ) || $this->getParameter( 'servedby' ) + ) ) { + $result->addValue( null, 'servedby', wfHostname(), ApiResult::NO_SIZE_CHECK ); } if ( $this->getParameter( 'curtimestamp' ) ) { @@ -1058,13 +1156,23 @@ class ApiMain extends ApiBase { ApiResult::NO_SIZE_CHECK ); } - $params = $this->extractRequestParams(); + if ( $this->getParameter( 'responselanginfo' ) ) { + $result->addValue( null, 'uselang', $this->getLanguage()->getCode(), + ApiResult::NO_SIZE_CHECK ); + $result->addValue( null, 'errorlang', $this->getErrorFormatter()->getLanguage()->getCode(), + ApiResult::NO_SIZE_CHECK ); + } + } - $this->mAction = $params['action']; + /** + * Set up for the execution. + * @return array + */ + protected function setupExecuteAction() { + $this->addRequestedFields(); - if ( !is_string( $this->mAction ) ) { - $this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' ); - } + $params = $this->extractRequestParams(); + $this->mAction = $params['action']; return $params; } @@ -1073,13 +1181,15 @@ class ApiMain extends ApiBase { * Set up the module for response * @return ApiBase The module that will handle this action * @throws MWException - * @throws UsageException + * @throws ApiUsageException */ protected function setupModule() { // Instantiate the module requested by the user $module = $this->mModuleMgr->getModule( $this->mAction, 'action' ); if ( $module === null ) { - $this->dieUsage( 'The API requires a valid action parameter', 'unknown_action' ); + $this->dieWithError( + [ 'apierror-unknownaction', wfEscapeWikiText( $this->mAction ) ], 'unknown_action' + ); } $moduleParams = $module->extractRequestParams(); @@ -1098,13 +1208,13 @@ class ApiMain extends ApiBase { } if ( !isset( $moduleParams['token'] ) ) { - $this->dieUsageMsg( [ 'missingparam', 'token' ] ); + $module->dieWithError( [ 'apierror-missingparam', 'token' ] ); } $module->requirePostedParameters( [ 'token' ] ); if ( !$module->validateToken( $moduleParams['token'], $moduleParams ) ) { - $this->dieUsageMsg( 'sessionfailure' ); + $module->dieWithError( 'apierror-badtoken' ); } } @@ -1128,10 +1238,10 @@ class ApiMain extends ApiBase { $response->header( 'X-Database-Lag: ' . intval( $lag ) ); if ( $this->getConfig()->get( 'ShowHostnames' ) ) { - $this->dieUsage( "Waiting for $host: $lag seconds lagged", 'maxlag' ); + $this->dieWithError( [ 'apierror-maxlag', $lag, $host ] ); } - $this->dieUsage( "Waiting for a database server: $lag seconds lagged", 'maxlag' ); + $this->dieWithError( [ 'apierror-maxlag-generic', $lag ], 'maxlag' ); } } @@ -1262,19 +1372,16 @@ class ApiMain extends ApiBase { if ( $module->isReadMode() && !User::isEveryoneAllowed( 'read' ) && !$user->isAllowed( 'read' ) ) { - $this->dieUsageMsg( 'readrequired' ); + $this->dieWithError( 'apierror-readapidenied' ); } if ( $module->isWriteMode() ) { if ( !$this->mEnableWrite ) { - $this->dieUsageMsg( 'writedisabled' ); + $this->dieWithError( 'apierror-noapiwrite' ); } elseif ( !$user->isAllowed( 'writeapi' ) ) { - $this->dieUsageMsg( 'writerequired' ); + $this->dieWithError( 'apierror-writeapidenied' ); } elseif ( $this->getRequest()->getHeader( 'Promise-Non-Write-API-Action' ) ) { - $this->dieUsage( - 'Promise-Non-Write-API-Action HTTP header cannot be sent to write API modules', - 'promised-nonwrite-api' - ); + $this->dieWithError( 'apierror-promised-nonwrite-api' ); } $this->checkReadOnly( $module ); @@ -1283,7 +1390,7 @@ class ApiMain extends ApiBase { // Allow extensions to stop execution for arbitrary reasons. $message = false; if ( !Hooks::run( 'ApiCheckCanExecute', [ $module, $user, &$message ] ) ) { - $this->dieUsageMsg( $message ); + $this->dieWithError( $message ); } } @@ -1329,12 +1436,9 @@ class ApiMain extends ApiBase { "Api request failed as read only because the following DBs are lagged: $laggedServers" ); - $parsed = $this->parseMsg( [ 'readonlytext' ] ); - $this->dieUsage( - $parsed['info'], - $parsed['code'], - /* http error */ - 0, + $this->dieWithError( + 'readonly_lag', + 'readonly', [ 'readonlyreason' => "Waiting for $numLagged lagged database(s)" ] ); } @@ -1350,12 +1454,12 @@ class ApiMain extends ApiBase { switch ( $params['assert'] ) { case 'user': if ( $user->isAnon() ) { - $this->dieUsage( 'Assertion that the user is logged in failed', 'assertuserfailed' ); + $this->dieWithError( 'apierror-assertuserfailed' ); } break; case 'bot': if ( !$user->isAllowed( 'bot' ) ) { - $this->dieUsage( 'Assertion that the user has the bot right failed', 'assertbotfailed' ); + $this->dieWithError( 'apierror-assertbotfailed' ); } break; } @@ -1363,9 +1467,8 @@ class ApiMain extends ApiBase { if ( isset( $params['assertuser'] ) ) { $assertUser = User::newFromName( $params['assertuser'], false ); if ( !$assertUser || !$this->getUser()->equals( $assertUser ) ) { - $this->dieUsage( - 'Assertion that the user is "' . $params['assertuser'] . '" failed', - 'assertnameduserfailed' + $this->dieWithError( + [ 'apierror-assertnameduserfailed', wfEscapeWikiText( $params['assertuser'] ) ] ); } } @@ -1381,7 +1484,7 @@ class ApiMain extends ApiBase { if ( !$request->wasPosted() && $module->mustBePosted() ) { // Module requires POST. GET request might still be allowed // if $wgDebugApi is true, otherwise fail. - $this->dieUsageMsgOrDebug( [ 'mustbeposted', $this->mAction ] ); + $this->dieWithErrorOrDebug( [ 'apierror-mustbeposted', $this->mAction ] ); } // See if custom printer is used @@ -1396,8 +1499,7 @@ class ApiMain extends ApiBase { ( $this->getUser()->isLoggedIn() && $this->getUser()->requiresHTTPS() ) ) ) { - $this->logFeatureUsage( 'https-expected' ); - $this->setWarning( 'HTTP used when HTTPS was expected' ); + $this->addDeprecation( 'apiwarn-deprecation-httpsexpected', 'https-expected' ); } } @@ -1481,7 +1583,9 @@ class ApiMain extends ApiBase { ]; if ( $e ) { - $logCtx['errorCodes'][] = $this->errorMessageFromException( $e )['code']; + foreach ( $this->errorMessagesFromException( $e ) as $msg ) { + $logCtx['errorCodes'][] = $msg->getApiCode(); + } } // Construct space separated message for 'api' log channel @@ -1560,9 +1664,7 @@ class ApiMain extends ApiBase { if ( $this->getRequest()->getArray( $name ) !== null ) { // See bug 10262 for why we don't just implode( '|', ... ) the // array. - $this->setWarning( - "Parameter '$name' uses unsupported PHP array syntax" - ); + $this->addWarning( [ 'apiwarn-unsupportedarray', $name ] ); } $ret = $default; } @@ -1602,8 +1704,7 @@ class ApiMain extends ApiBase { if ( !$this->mInternalMode ) { // Printer has not yet executed; don't warn that its parameters are unused - $printerParams = array_map( - [ $this->mPrinter, 'encodeParamName' ], + $printerParams = $this->mPrinter->encodeParamName( array_keys( $this->mPrinter->getFinalParams() ?: [] ) ); $unusedParams = array_diff( $allParams, $paramsUsed, $printerParams ); @@ -1612,8 +1713,11 @@ class ApiMain extends ApiBase { } if ( count( $unusedParams ) ) { - $s = count( $unusedParams ) > 1 ? 's' : ''; - $this->setWarning( "Unrecognized parameter$s: '" . implode( $unusedParams, "', '" ) . "'" ); + $this->addWarning( [ + 'apierror-unrecognizedparams', + Message::listParam( array_map( 'wfEscapeWikiText', $unusedParams ), 'comma' ), + count( $unusedParams ) + ] ); } } @@ -1624,7 +1728,7 @@ class ApiMain extends ApiBase { */ protected function printResult( $httpCode = 0 ) { if ( $this->getConfig()->get( 'DebugAPI' ) !== false ) { - $this->setWarning( 'SECURITY WARNING: $wgDebugAPI is enabled' ); + $this->addWarning( 'apiwarn-wgDebugAPI' ); } $printer = $this->mPrinter; @@ -1678,9 +1782,20 @@ class ApiMain extends ApiBase { 'requestid' => null, 'servedby' => false, 'curtimestamp' => false, + 'responselanginfo' => false, 'origin' => null, 'uselang' => [ - ApiBase::PARAM_DFLT => 'user', + ApiBase::PARAM_DFLT => self::API_DEFAULT_USELANG, + ], + 'errorformat' => [ + ApiBase::PARAM_TYPE => [ 'plaintext', 'wikitext', 'html', 'raw', 'none', 'bc' ], + ApiBase::PARAM_DFLT => 'bc', + ], + 'errorlang' => [ + ApiBase::PARAM_DFLT => 'uselang', + ], + 'errorsuselocal' => [ + ApiBase::PARAM_DFLT => false, ], ]; } @@ -1732,7 +1847,7 @@ class ApiMain extends ApiBase { $help['permissions'] .= Html::rawElement( 'dd', null, $this->msg( 'api-help-permissions-granted-to' ) ->numParams( count( $groups ) ) - ->params( $this->getLanguage()->commaList( $groups ) ) + ->params( Message::listParam( $groups ) ) ->parse() ); } @@ -1831,70 +1946,6 @@ class ApiMain extends ApiBase { } } -/** - * This exception will be thrown when dieUsage is called to stop module execution. - * - * @ingroup API - */ -class UsageException extends MWException { - - private $mCodestr; - - /** - * @var null|array - */ - private $mExtraData; - - /** - * @param string $message - * @param string $codestr - * @param int $code - * @param array|null $extradata - */ - public function __construct( $message, $codestr, $code = 0, $extradata = null ) { - parent::__construct( $message, $code ); - $this->mCodestr = $codestr; - $this->mExtraData = $extradata; - - // This should never happen, so throw an exception about it that will - // hopefully get logged with a backtrace (T138585) - if ( !is_string( $codestr ) || $codestr === '' ) { - throw new InvalidArgumentException( 'Invalid $codestr, was ' . - ( $codestr === '' ? 'empty string' : gettype( $codestr ) ) - ); - } - } - - /** - * @return string - */ - public function getCodeString() { - return $this->mCodestr; - } - - /** - * @return array - */ - public function getMessageArray() { - $result = [ - 'code' => $this->mCodestr, - 'info' => $this->getMessage() - ]; - if ( is_array( $this->mExtraData ) ) { - $result = array_merge( $result, $this->mExtraData ); - } - - return $result; - } - - /** - * @return string - */ - public function __toString() { - return "{$this->getCodeString()}: {$this->getMessage()}"; - } -} - /** * For really cool vim folding this needs to be at the end: * vim: foldmarker=@{,@} foldmethod=marker