* @param ApiResult $result Into which data will be added
* @param Language $lang Used for i18n
* @param string $format
- * - text: Error message as wikitext
+ * - plaintext: Error message as something vaguely like plaintext
+ * (it's basically wikitext with HTML tags stripped and entities decoded)
+ * - wikitext: Error message as wikitext
* - html: Error message as HTML
* - raw: Raw message key and parameters, no human-readable text
* - none: Code and data only, no human-readable text
$this->format = $format;
}
+ /**
+ * Fetch the Language for this formatter
+ * @since 1.29
+ * @return Language
+ */
+ public function getLanguage() {
+ return $this->lang;
+ }
+
/**
* Fetch a dummy title to set on Messages
* @return Title
/**
* Add a warning to the result
- * @param string $moduleName
- * @param MessageSpecifier|array|string $msg i18n message for the warning
- * @param string $code Machine-readable code for the warning. Defaults as
- * for IApiMessage::getApiCode().
- * @param array $data Machine-readable data for the warning, if any.
- * Uses IApiMessage::getApiData() if $msg implements that interface.
+ * @param string|null $modulePath
+ * @param Message|array|string $msg Warning message. See ApiMessage::create().
+ * @param string|null $code See ApiMessage::create().
+ * @param array|null $data See ApiMessage::create().
*/
- public function addWarning( $moduleName, $msg, $code = null, $data = null ) {
+ public function addWarning( $modulePath, $msg, $code = null, $data = null ) {
$msg = ApiMessage::create( $msg, $code, $data )
->inLanguage( $this->lang )
->title( $this->getDummyTitle() )
->useDatabase( $this->useDB );
- $this->addWarningOrError( 'warning', $moduleName, $msg );
+ $this->addWarningOrError( 'warning', $modulePath, $msg );
}
/**
* Add an error to the result
- * @param string $moduleName
- * @param MessageSpecifier|array|string $msg i18n message for the error
- * @param string $code Machine-readable code for the warning. Defaults as
- * for IApiMessage::getApiCode().
- * @param array $data Machine-readable data for the warning, if any.
- * Uses IApiMessage::getApiData() if $msg implements that interface.
+ * @param string|null $modulePath
+ * @param Message|array|string $msg Warning message. See ApiMessage::create().
+ * @param string|null $code See ApiMessage::create().
+ * @param array|null $data See ApiMessage::create().
*/
- public function addError( $moduleName, $msg, $code = null, $data = null ) {
+ public function addError( $modulePath, $msg, $code = null, $data = null ) {
$msg = ApiMessage::create( $msg, $code, $data )
->inLanguage( $this->lang )
->title( $this->getDummyTitle() )
->useDatabase( $this->useDB );
- $this->addWarningOrError( 'error', $moduleName, $msg );
+ $this->addWarningOrError( 'error', $modulePath, $msg );
}
/**
- * Add warnings and errors from a Status object to the result
- * @param string $moduleName
- * @param Status $status
+ * Add warnings and errors from a StatusValue object to the result
+ * @param string|null $modulePath
+ * @param StatusValue $status
* @param string[] $types 'warning' and/or 'error'
*/
public function addMessagesFromStatus(
- $moduleName, Status $status, $types = [ 'warning', 'error' ]
+ $modulePath, StatusValue $status, $types = [ 'warning', 'error' ]
) {
- if ( $status->isGood() || !$status->errors ) {
+ if ( $status->isGood() || !$status->getErrors() ) {
return;
}
$types = (array)$types;
- foreach ( $status->errors as $error ) {
+ foreach ( $status->getErrors() as $error ) {
if ( !in_array( $error['type'], $types, true ) ) {
continue;
}
$tag = 'warning';
}
- if ( is_array( $error ) && isset( $error['message'] ) ) {
- // Normal case
- if ( $error['message'] instanceof Message ) {
- $msg = ApiMessage::create( $error['message'], null, [] );
- } else {
- $args = isset( $error['params'] ) ? $error['params'] : [];
- array_unshift( $args, $error['message'] );
- $error += [ 'params' => [] ];
- $msg = ApiMessage::create( $args, null, [] );
+ $msg = ApiMessage::create( $error )
+ ->inLanguage( $this->lang )
+ ->title( $this->getDummyTitle() )
+ ->useDatabase( $this->useDB );
+ $this->addWarningOrError( $tag, $modulePath, $msg );
+ }
+ }
+
+ /**
+ * Get an ApiMessage from an exception
+ * @since 1.29
+ * @param Exception|Throwable $exception
+ * @param array $options
+ * - wrap: (string|array|MessageSpecifier) Used to wrap the exception's
+ * message if it's not an ILocalizedException. The exception's message
+ * will be added as the final parameter.
+ * - code: (string) Default code
+ * - data: (array) Default extra data
+ * @return IApiMessage
+ */
+ public function getMessageFromException( $exception, array $options = [] ) {
+ $options += [ 'code' => null, 'data' => [] ];
+
+ if ( $exception instanceof ILocalizedException ) {
+ $msg = $exception->getMessageObject();
+ $params = [];
+ } else {
+ // Extract code and data from the exception, if applicable
+ if ( $exception instanceof UsageException ) {
+ $data = $exception->getMessageArray();
+ if ( !$options['code'] ) {
+ $options['code'] = $data['code'];
}
- } elseif ( is_array( $error ) ) {
- // Weird case handled by Message::getErrorMessage
- $msg = ApiMessage::create( $error, null, [] );
- } else {
- // Another weird case handled by Message::getErrorMessage
- $msg = ApiMessage::create( $error, null, [] );
+ unset( $data['code'], $data['info'] );
+ $options['data'] = array_merge( $data, $options['data'] );
}
- $msg->inLanguage( $this->lang )
- ->title( $this->getDummyTitle() )
- ->useDatabase( $this->useDB );
- $this->addWarningOrError( $tag, $moduleName, $msg );
+ if ( isset( $options['wrap'] ) ) {
+ $msg = $options['wrap'];
+ } else {
+ $msg = new RawMessage( '$1' );
+ if ( !isset( $options['code'] ) ) {
+ $options['code'] = 'internal_api_error_' . get_class( $exception );
+ }
+ }
+ $params = [ wfEscapeWikiText( $exception->getMessage() ) ];
}
+ return ApiMessage::create( $msg, $options['code'], $options['data'] )
+ ->params( $params )
+ ->inLanguage( $this->lang )
+ ->title( $this->getDummyTitle() )
+ ->useDatabase( $this->useDB );
}
/**
- * Format messages from a Status as an array
- * @param Status $status
+ * Format an exception as an array
+ * @since 1.29
+ * @param Exception|Throwable $exception
+ * @param array $options See self::getMessageFromException(), plus
+ * - format: (string) Format override
+ * @return array
+ */
+ public function formatException( $exception, array $options = [] ) {
+ return $this->formatMessage(
+ $this->getMessageFromException( $exception, $options ),
+ isset( $options['format'] ) ? $options['format'] : null
+ );
+ }
+
+ /**
+ * Format a message as an array
+ * @param Message|array|string $msg Message. See ApiMessage::create().
+ * @param string|null $format
+ * @return array
+ */
+ public function formatMessage( $msg, $format = null ) {
+ $msg = ApiMessage::create( $msg )
+ ->inLanguage( $this->lang )
+ ->title( $this->getDummyTitle() )
+ ->useDatabase( $this->useDB );
+ return $this->formatMessageInternal( $msg, $format ?: $this->format );
+ }
+
+ /**
+ * Format messages from a StatusValue as an array
+ * @param StatusValue $status
* @param string $type 'warning' or 'error'
* @param string|null $format
* @return array
*/
- public function arrayFromStatus( Status $status, $type = 'error', $format = null ) {
- if ( $status->isGood() || !$status->errors ) {
+ public function arrayFromStatus( StatusValue $status, $type = 'error', $format = null ) {
+ if ( $status->isGood() || !$status->getErrors() ) {
return [];
}
$formatter = new ApiErrorFormatter(
$result, $this->lang, $format ?: $this->format, $this->useDB
);
- $formatter->addMessagesFromStatus( 'dummy', $status, [ $type ] );
+ $formatter->addMessagesFromStatus( null, $status, [ $type ] );
switch ( $type ) {
case 'error':
- return (array)$result->getResultData( [ 'errors', 'dummy' ] );
+ return (array)$result->getResultData( [ 'errors' ] );
case 'warning':
- return (array)$result->getResultData( [ 'warnings', 'dummy' ] );
+ return (array)$result->getResultData( [ 'warnings' ] );
}
}
/**
- * Actually add the warning or error to the result
- * @param string $tag 'warning' or 'error'
- * @param string $moduleName
+ * Turn wikitext into something resembling plaintext
+ * @since 1.29
+ * @param string $text
+ * @return string
+ */
+ public static function stripMarkup( $text ) {
+ // Turn semantic quoting tags to quotes
+ $ret = preg_replace( '!</?(var|kbd|samp|code)>!', '"', $text );
+
+ // Strip tags and decode.
+ $ret = html_entity_decode( strip_tags( $ret ), ENT_QUOTES | ENT_HTML5 );
+
+ return $ret;
+ }
+
+ /**
+ * Format a Message object for raw format
+ * @param MessageSpecifier $msg
+ * @return array
+ */
+ private function formatRawMessage( MessageSpecifier $msg ) {
+ $ret = [
+ 'key' => $msg->getKey(),
+ 'params' => $msg->getParams(),
+ ];
+ ApiResult::setIndexedTagName( $ret['params'], 'param' );
+
+ // Transform Messages as parameters in the style of Message::fooParam().
+ foreach ( $ret['params'] as $i => $param ) {
+ if ( $param instanceof MessageSpecifier ) {
+ $ret['params'][$i] = [ 'message' => $this->formatRawMessage( $param ) ];
+ }
+ }
+ return $ret;
+ }
+
+ /**
+ * Format a message as an array
+ * @since 1.29
* @param ApiMessage|ApiRawMessage $msg
+ * @param string|null $format
+ * @return array
*/
- protected function addWarningOrError( $tag, $moduleName, $msg ) {
+ protected function formatMessageInternal( $msg, $format ) {
$value = [ 'code' => $msg->getApiCode() ];
- switch ( $this->format ) {
+ switch ( $format ) {
+ case 'plaintext':
+ $value += [
+ 'text' => self::stripMarkup( $msg->text() ),
+ ApiResult::META_CONTENT => 'text',
+ ];
+ break;
+
case 'wikitext':
$value += [
'text' => $msg->text(),
break;
case 'raw':
- $value += [
- 'key' => $msg->getKey(),
- 'params' => $msg->getParams(),
- ];
- ApiResult::setIndexedTagName( $value['params'], 'param' );
+ $value += $this->formatRawMessage( $msg );
break;
case 'none':
break;
}
- $value += $msg->getApiData();
+ $data = $msg->getApiData();
+ if ( $data ) {
+ $value['data'] = $msg->getApiData() + [
+ ApiResult::META_TYPE => 'assoc',
+ ];
+ }
+ return $value;
+ }
+
+ /**
+ * Actually add the warning or error to the result
+ * @param string $tag 'warning' or 'error'
+ * @param string|null $modulePath
+ * @param ApiMessage|ApiRawMessage $msg
+ */
+ protected function addWarningOrError( $tag, $modulePath, $msg ) {
+ $value = $this->formatMessageInternal( $msg, $this->format );
+ if ( $modulePath !== null ) {
+ $value += [ 'module' => $modulePath ];
+ }
- $path = [ $tag . 's', $moduleName ];
+ $path = [ $tag . 's' ];
$existing = $this->result->getResultData( $path );
if ( $existing === null || !in_array( $value, $existing ) ) {
$flags = ApiResult::NO_SIZE_CHECK;
parent::__construct( $result, Language::factory( 'en' ), 'none', false );
}
- public function arrayFromStatus( Status $status, $type = 'error', $format = null ) {
- if ( $status->isGood() || !$status->errors ) {
+ public function arrayFromStatus( StatusValue $status, $type = 'error', $format = null ) {
+ if ( $status->isGood() || !$status->getErrors() ) {
return [];
}
$result = [];
foreach ( $status->getErrorsByType( $type ) as $error ) {
- if ( $error['message'] instanceof Message ) {
- $error = [
- 'message' => $error['message']->getKey(),
- 'params' => $error['message']->getParams(),
- ] + $error;
- }
+ $msg = ApiMessage::create( $error );
+ $error = [
+ 'message' => $msg->getKey(),
+ 'params' => $msg->getParams(),
+ 'code' => $msg->getApiCode(),
+ ] + $error;
ApiResult::setIndexedTagName( $error['params'], 'param' );
$result[] = $error;
}
return $result;
}
- protected function addWarningOrError( $tag, $moduleName, $msg ) {
- $value = $msg->plain();
+ protected function formatMessageInternal( $msg, $format ) {
+ return [
+ 'code' => $msg->getApiCode(),
+ 'info' => $msg->text(),
+ ] + $msg->getApiData();
+ }
+
+ /**
+ * Format an exception as an array
+ * @since 1.29
+ * @param Exception|Throwable $exception
+ * @param array $options See parent::formatException(), plus
+ * - bc: (bool) Return only the string, not an array
+ * @return array|string
+ */
+ public function formatException( $exception, array $options = [] ) {
+ $ret = parent::formatException( $exception, $options );
+ return empty( $options['bc'] ) ? $ret : $ret['info'];
+ }
+
+ protected function addWarningOrError( $tag, $modulePath, $msg ) {
+ $value = self::stripMarkup( $msg->text() );
if ( $tag === 'error' ) {
// In BC mode, only one error
- $code = $msg->getApiCode();
- if ( isset( ApiBase::$messageMap[$code] ) ) {
- // Backwards compatibility
- $code = ApiBase::$messageMap[$code]['code'];
- }
-
$value = [
- 'code' => $code,
+ 'code' => $msg->getApiCode(),
'info' => $value,
] + $msg->getApiData();
$this->result->addValue( null, 'error', $value,
ApiResult::OVERRIDE | ApiResult::ADD_ON_TOP | ApiResult::NO_SIZE_CHECK );
} else {
+ if ( $modulePath === null ) {
+ $moduleName = 'unknown';
+ } else {
+ $i = strrpos( $modulePath, '+' );
+ $moduleName = $i === false ? $modulePath : substr( $modulePath, $i + 1 );
+ }
+
// Don't add duplicate warnings
$tag .= 's';
$path = [ $tag, $moduleName ];