wiki.techinc.nl/includes/Rest/Handler/PageSourceHandler.php
Wendy Quarshie 87b34ec850 REST: JSON schema definitions for additional response bodies
Bug: T376603
Depends-On: I4193b9be4516717c7ce423131370a7d0b6ea8962
Change-Id: Ic2d9471ad446eb5f9d5e7072f1ef93f7196a20f8
2025-07-15 11:40:08 +01:00

187 lines
5.3 KiB
PHP

<?php
namespace MediaWiki\Rest\Handler;
use LogicException;
use MediaWiki\Page\PageReference;
use MediaWiki\Rest\Handler\Helper\PageContentHelper;
use MediaWiki\Rest\Handler\Helper\PageRedirectHelper;
use MediaWiki\Rest\Handler\Helper\PageRestHelperFactory;
use MediaWiki\Rest\LocalizedHttpException;
use MediaWiki\Rest\Response;
use MediaWiki\Rest\SimpleHandler;
use MediaWiki\Title\TitleFormatter;
use Wikimedia\Message\MessageValue;
/**
* Handler class for Core REST API Page Source endpoint with the following routes:
* - /page/{title}
* - /page/{title}/bare
*/
class PageSourceHandler extends SimpleHandler {
private TitleFormatter $titleFormatter;
private PageRestHelperFactory $helperFactory;
private PageContentHelper $contentHelper;
public function __construct(
TitleFormatter $titleFormatter,
PageRestHelperFactory $helperFactory
) {
$this->titleFormatter = $titleFormatter;
$this->contentHelper = $helperFactory->newPageContentHelper();
$this->helperFactory = $helperFactory;
}
private function getRedirectHelper(): PageRedirectHelper {
return $this->helperFactory->newPageRedirectHelper(
$this->getResponseFactory(),
$this->getRouter(),
$this->getPath(),
$this->getRequest()
);
}
protected function postValidationSetup() {
$this->contentHelper->init( $this->getAuthority(), $this->getValidatedParams() );
}
/**
* @param PageReference $page
* @return string
*/
private function constructHtmlUrl( PageReference $page ): string {
// TODO: once legacy "v1" routes are removed, just use the path prefix from the module.
$pathPrefix = $this->getModule()->getPathPrefix();
if ( strlen( $pathPrefix ) == 0 ) {
$pathPrefix = 'v1';
}
return $this->getRouter()->getRouteUrl(
'/' . $pathPrefix . '/page/{title}/html',
[ 'title' => $this->titleFormatter->getPrefixedText( $page ) ]
);
}
/**
* @return Response
* @throws LocalizedHttpException
*/
public function run(): Response {
$this->contentHelper->checkAccess();
$page = $this->contentHelper->getPageIdentity();
if ( !$page->exists() ) {
// We may get here for "known" but non-existing pages, such as
// message pages. Since there is no page, we should still return
// a 404. See T349677 for discussion.
$titleText = $this->contentHelper->getTitleText() ?? '(unknown)';
throw new LocalizedHttpException(
MessageValue::new( 'rest-nonexistent-title' )
->plaintextParams( $titleText ),
404
);
}
$redirectHelper = $this->getRedirectHelper();
'@phan-var \MediaWiki\Page\ExistingPageRecord $page';
$redirectResponse = $redirectHelper->createNormalizationRedirectResponseIfNeeded(
$page,
$this->contentHelper->getTitleText()
);
if ( $redirectResponse !== null ) {
return $redirectResponse;
}
$outputMode = $this->getOutputMode();
switch ( $outputMode ) {
case 'restbase': // compatibility for restbase migration
$body = [ 'items' => [ $this->contentHelper->constructRestbaseCompatibleMetadata() ] ];
break;
case 'bare':
$body = $this->contentHelper->constructMetadata();
$body['html_url'] = $this->constructHtmlUrl( $page );
break;
case 'source':
$content = $this->contentHelper->getContent();
$body = $this->contentHelper->constructMetadata();
$body['source'] = $content->getText();
break;
default:
throw new LogicException( "Unknown HTML type $outputMode" );
}
// If param redirect=no is present, that means this page can be a redirect
// check for a redirectTargetUrl and send it to the body as `redirect_target`
'@phan-var \MediaWiki\Page\ExistingPageRecord $page';
$redirectTargetUrl = $redirectHelper->getWikiRedirectTargetUrl( $page );
if ( $redirectTargetUrl ) {
$body['redirect_target'] = $redirectTargetUrl;
}
$response = $this->getResponseFactory()->createJson( $body );
$this->contentHelper->setCacheControl( $response );
return $response;
}
/**
* Returns an ETag representing a page's source. The ETag assumes a page's source has changed
* if the latest revision of a page has been made private, un-readable for another reason,
* or a newer revision exists.
* @return string|null
*/
protected function getETag(): ?string {
return $this->contentHelper->getETag();
}
/**
* @return string|null
*/
protected function getLastModified(): ?string {
return $this->contentHelper->getLastModified();
}
private function getOutputMode(): string {
if ( $this->getRouter()->isRestbaseCompatEnabled( $this->getRequest() ) ) {
return 'restbase';
}
return $this->getConfig()['format'];
}
public function needsWriteAccess(): bool {
return false;
}
public function getParamSettings(): array {
return $this->contentHelper->getParamSettings();
}
/**
* @return bool
*/
protected function hasRepresentation() {
return $this->contentHelper->hasContent();
}
public function getResponseBodySchemaFileName( string $method ): ?string {
// This does not include restbase compatibility mode, which is triggered by request
// headers. Presumably, such callers will look at the RESTBase spec instead.
switch ( $this->getConfig()['format'] ) {
case 'bare':
$schema = 'includes/Rest/Handler/Schema/ExistingPageBare.json';
break;
case 'source':
$schema = 'includes/Rest/Handler/Schema/ExistingPageSource.json';
break;
default:
$schema = null;
break;
}
return $schema;
}
}