1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
<?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();
}
}
|