use Exception;
use HttpStatus;
use InvalidArgumentException;
+use LanguageCode;
use MWExceptionHandler;
use stdClass;
use Throwable;
+use Wikimedia\Message\ITextFormatter;
+use Wikimedia\Message\MessageValue;
/**
* Generates standardized response objects.
*/
class ResponseFactory {
-
const CT_PLAIN = 'text/plain; charset=utf-8';
const CT_HTML = 'text/html; charset=utf-8';
const CT_JSON = 'application/json';
+ /** @var ITextFormatter[] */
+ private $textFormatters;
+
+ /**
+ * @param ITextFormatter[] $textFormatters
+ */
+ public function __construct( $textFormatters ) {
+ $this->textFormatters = $textFormatters;
+ }
+
/**
* Encode a stdClass object or array to a JSON string
*
* the new URL in the future. 301 redirects tend to get cached and are hard to undo.
* Client behavior for methods other than GET/HEAD is not well-defined and this type
* of response should be avoided in such cases.
- * @param string $target Redirect URL (can be relative)
+ * @param string $target Redirect target (an absolute URL)
* @return Response
*/
public function createPermanentRedirect( $target ) {
return $response;
}
+ /**
+ * Creates a temporary (302) redirect.
+ * HTTP 302 was underspecified and has been superseded by 303 (when the redirected request
+ * should be a GET, regardless of what the current request is) and 307 (when the method should
+ * not be changed), but might still be needed for HTTP 1.0 clients or to match legacy behavior.
+ * @param string $target Redirect target (an absolute URL)
+ * @return Response
+ * @see self::createTemporaryRedirect()
+ * @see self::createSeeOther()
+ */
+ public function createLegacyTemporaryRedirect( $target ) {
+ $response = $this->createRedirectBase( $target );
+ $response->setStatus( 302 );
+ return $response;
+ }
+
/**
* Creates a temporary (307) redirect.
* This indicates that the operation the client was trying to perform can temporarily
* be achieved by using a different URL. Clients will preserve the request method when
* retrying the request with the new URL.
- * @param string $target Redirect URL (can be relative)
+ * @param string $target Redirect target (an absolute URL)
* @return Response
*/
public function createTemporaryRedirect( $target ) {
* This indicates that the target resource might be of interest to the client, without
* necessarily implying that it is the same resource. The client will always use GET
* (or HEAD) when following the redirection. Useful for GET-after-POST.
- * @param string $target Redirect URL (can be relative)
+ * @param string $target Redirect target (an absolute URL)
* @return Response
*/
public function createSeeOther( $target ) {
return $response;
}
+ /**
+ * Create an HTTP 4xx or 5xx response with error message localisation
+ */
+ public function createLocalizedHttpError( $errorCode, MessageValue $messageValue ) {
+ return $this->createHttpError( $errorCode, $this->formatMessage( $messageValue ) );
+ }
+
/**
* Turn an exception into a JSON error response.
* @param Exception|Throwable $exception
* @return Response
*/
public function createFromException( $exception ) {
- if ( $exception instanceof HttpException ) {
+ if ( $exception instanceof LocalizedHttpException ) {
+ $response = $this->createLocalizedHttpError( $exception->getCode(),
+ $exception->getMessageValue() );
+ } elseif ( $exception instanceof HttpException ) {
// FIXME can HttpException represent 2xx or 3xx responses?
- $response = $this->createHttpError( $exception->getCode(),
- [ 'message' => $exception->getMessage() ] );
+ $response = $this->createHttpError(
+ $exception->getCode(),
+ array_merge(
+ [ 'message' => $exception->getMessage() ],
+ (array)$exception->getErrorData()
+ )
+ );
} else {
$response = $this->createHttpError( 500, [
'message' => 'Error: exception of type ' . get_class( $exception ),
return "<!doctype html><title>Redirect</title><a href=\"$url\">$url</a>";
}
+ public function formatMessage( MessageValue $messageValue ) {
+ if ( !$this->textFormatters ) {
+ // For unit tests
+ return [];
+ }
+ $translations = [];
+ foreach ( $this->textFormatters as $formatter ) {
+ $lang = LanguageCode::bcp47( $formatter->getLangCode() );
+ $messageText = $formatter->format( $messageValue );
+ $translations[$lang] = $messageText;
+ }
+ return [ 'messageTranslations' => $translations ];
+ }
+
}