/**
* Set the continuation manager
- * @param ApiContinuationManager|null
+ * @param ApiContinuationManager|null $manager
*/
public function setContinuationManager( $manager ) {
if ( $manager !== null ) {
// T65145: Rollback any open database transactions
if ( !( $e instanceof ApiUsageException || $e instanceof UsageException ) ) {
// UsageExceptions are intentional, so don't rollback if that's the case
- try {
- MWExceptionHandler::rollbackMasterChangesAndLog( $e );
- } catch ( DBError $e2 ) {
- // Rollback threw an exception too. Log it, but don't interrupt
- // our regularly scheduled exception handling.
- MWExceptionHandler::logException( $e2 );
- }
+ MWExceptionHandler::rollbackMasterChangesAndLog( $e );
}
// Allow extra cleanup and logging
Hooks::run( 'ApiMain::onException', [ $this, $e ] );
- // Log it
- if ( !( $e instanceof ApiUsageException || $e instanceof UsageException ) ) {
- MWExceptionHandler::logException( $e );
- }
-
// Handle any kind of exception by outputting properly formatted error message.
// If this fails, an unhandled exception should be thrown so that global error
// handler will process and log it.
$request = $this->getRequest();
$response = $request->response();
- $matchOrigin = false;
+ $matchedOrigin = false;
$allowTiming = false;
$varyOrigin = true;
if ( $originParam === '*' ) {
// Request for anonymous CORS
- $matchOrigin = true;
+ // Technically we should check for the presence of an Origin header
+ // and not process it as CORS if it's not set, but that would
+ // require us to vary on Origin for all 'origin=*' requests which
+ // we don't want to do.
+ $matchedOrigin = true;
$allowOrigin = '*';
$allowCredentials = 'false';
$varyOrigin = false; // No need to vary
}
$config = $this->getConfig();
- $matchOrigin = count( $origins ) === 1 && self::matchOrigin(
+ $matchedOrigin = count( $origins ) === 1 && self::matchOrigin(
$originParam,
$config->get( 'CrossSiteAJAXdomains' ),
$config->get( 'CrossSiteAJAXdomainExceptions' )
$allowTiming = $originHeader;
}
- if ( $matchOrigin ) {
+ if ( $matchedOrigin ) {
$requestedMethod = $request->getHeader( 'Access-Control-Request-Method' );
$preflight = $request->getMethod() === 'OPTIONS' && $requestedMethod !== false;
if ( $preflight ) {
// This is a CORS preflight request
if ( $requestedMethod !== 'POST' && $requestedMethod !== 'GET' ) {
// If method is not a case-sensitive match, do not set any additional headers and terminate.
+ $response->header( 'MediaWiki-CORS-Rejection: Unsupported method requested in preflight' );
return true;
}
// We allow the actual request to send the following headers
$requestedHeaders = $request->getHeader( 'Access-Control-Request-Headers' );
if ( $requestedHeaders !== false ) {
if ( !self::matchRequestedHeaders( $requestedHeaders ) ) {
+ $response->header( 'MediaWiki-CORS-Rejection: Unsupported header requested in preflight' );
return true;
}
$response->header( 'Access-Control-Allow-Headers: ' . $requestedHeaders );
// We only allow the actual request to be GET or POST
$response->header( 'Access-Control-Allow-Methods: POST, GET' );
+ } elseif ( $request->getMethod() !== 'POST' && $request->getMethod() !== 'GET' ) {
+ // Unsupported non-preflight method, don't handle it as CORS
+ $response->header(
+ 'MediaWiki-CORS-Rejection: Unsupported method for simple request or actual request'
+ );
+ return true;
}
$response->header( "Access-Control-Allow-Origin: $allowOrigin" );
. 'MediaWiki-Login-Suppressed'
);
}
+ } else {
+ $response->header( 'MediaWiki-CORS-Rejection: Origin mismatch' );
}
if ( $varyOrigin ) {
// None of the rest have any messages for non-error types
} elseif ( $e instanceof UsageException ) {
// User entered incorrect parameters - generate error response
- $data = $e->getMessageArray();
+ $data = MediaWiki\quietCall( [ $e, 'getMessageArray' ] );
$code = $data['code'];
$info = $data['info'];
unset( $data['code'], $data['info'] );
ApiBase::PARAM_TYPE => 'submodule',
],
'format' => [
- ApiBase::PARAM_DFLT => ApiMain::API_DEFAULT_FORMAT,
+ ApiBase::PARAM_DFLT => self::API_DEFAULT_FORMAT,
ApiBase::PARAM_TYPE => 'submodule',
],
'maxlag' => [
$header = $this->msg( 'api-help-datatypes-header' )->parse();
- // Add an additional span with sanitized ID
- if ( !$this->getConfig()->get( 'ExperimentalHtmlIds' ) ) {
- $header = Html::element( 'span', [ 'id' => Sanitizer::escapeId( 'main/datatypes' ) ] ) .
- $header;
- }
- $help['datatypes'] .= Html::rawElement( 'h' . min( 6, $level ),
- [ 'id' => 'main/datatypes', 'class' => 'apihelp-header' ],
- $header
+ $id = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_PRIMARY );
+ $idFallback = Sanitizer::escapeIdForAttribute( 'main/datatypes', Sanitizer::ID_FALLBACK );
+ $headline = Linker::makeHeadline( min( 6, $level ),
+ ' class="apihelp-header"',
+ $id,
+ $header,
+ '',
+ $idFallback
);
+ // Ensure we have a sane anchor
+ if ( $id !== 'main/datatypes' && $idFallback !== 'main/datatypes' ) {
+ $headline = '<div id="main/datatypes"></div>' . $headline;
+ }
+ $help['datatypes'] .= $headline;
$help['datatypes'] .= $this->msg( 'api-help-datatypes' )->parseAsBlock();
if ( !isset( $tocData['main/datatypes'] ) ) {
$tocnumber[$level]++;
];
}
- // Add an additional span with sanitized ID
- if ( !$this->getConfig()->get( 'ExperimentalHtmlIds' ) ) {
- $header = Html::element( 'span', [ 'id' => Sanitizer::escapeId( 'main/credits' ) ] ) .
- $header;
- }
$header = $this->msg( 'api-credits-header' )->parse();
- $help['credits'] .= Html::rawElement( 'h' . min( 6, $level ),
- [ 'id' => 'main/credits', 'class' => 'apihelp-header' ],
- $header
+ $id = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_PRIMARY );
+ $idFallback = Sanitizer::escapeIdForAttribute( 'main/credits', Sanitizer::ID_FALLBACK );
+ $headline = Linker::makeHeadline( min( 6, $level ),
+ ' class="apihelp-header"',
+ $id,
+ $header,
+ '',
+ $idFallback
);
+ // Ensure we have a sane anchor
+ if ( $id !== 'main/credits' && $idFallback !== 'main/credits' ) {
+ $headline = '<div id="main/credits"></div>' . $headline;
+ }
+ $help['credits'] .= $headline;
$help['credits'] .= $this->msg( 'api-credits' )->useDatabase( false )->parseAsBlock();
if ( !isset( $tocData['main/credits'] ) ) {
$tocnumber[$level]++;