PageDataRequestHandler.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <?php
  2. /**
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License as published by
  5. * the Free Software Foundation; either version 2 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along
  14. * with this program; if not, write to the Free Software Foundation, Inc.,
  15. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. * http://www.gnu.org/copyleft/gpl.html
  17. *
  18. * @file
  19. */
  20. use Wikimedia\Http\HttpAcceptParser;
  21. use Wikimedia\Http\HttpAcceptNegotiator;
  22. /**
  23. * Request handler implementing a data interface for mediawiki pages.
  24. *
  25. * @author Daniel Kinzler
  26. * @author Amir Sarabadanai
  27. */
  28. class PageDataRequestHandler {
  29. /**
  30. * Checks whether the request is complete, i.e. whether it contains all information needed
  31. * to reply with page data.
  32. *
  33. * This does not check whether the request is valid and will actually produce a successful
  34. * response.
  35. *
  36. * @param string|null $subPage
  37. * @param WebRequest $request
  38. *
  39. * @return bool
  40. */
  41. public function canHandleRequest( $subPage, WebRequest $request ) {
  42. if ( $subPage === '' || $subPage === null ) {
  43. return $request->getText( 'target' ) !== '';
  44. }
  45. $parts = explode( '/', $subPage, 2 );
  46. $slot = $parts[0];
  47. $title = $parts[1] ?? '';
  48. return ( $slot === 'main' || $slot === '' ) && $title !== '';
  49. }
  50. /**
  51. * Main method for handling requests.
  52. *
  53. * @param string|null $subPage
  54. * @param WebRequest $request The request parameters. Known parameters are:
  55. * - title: the page title
  56. * - format: the format
  57. * - oldid|revision: the revision ID
  58. * @param OutputPage $output
  59. *
  60. * @note Instead of an output page, a WebResponse could be sufficient, but
  61. * redirect logic is currently implemented in OutputPage.
  62. *
  63. * @throws HttpError
  64. */
  65. public function handleRequest( $subPage, WebRequest $request, OutputPage $output ) {
  66. // No matter what: The response is always public
  67. $output->getRequest()->response()->header( 'Access-Control-Allow-Origin: *' );
  68. if ( !$this->canHandleRequest( $subPage, $request ) ) {
  69. throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $subPage ) );
  70. }
  71. $revision = 0;
  72. if ( $subPage !== '' ) {
  73. $parts = explode( '/', $subPage, 2 );
  74. $title = $parts[1] ?? '';
  75. } else {
  76. $title = $request->getText( 'target' );
  77. }
  78. $revision = $request->getInt( 'oldid', $revision );
  79. $revision = $request->getInt( 'revision', $revision );
  80. if ( $title === null || $title === '' ) {
  81. //TODO: different error message?
  82. throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $title ) );
  83. }
  84. try {
  85. $title = Title::newFromTextThrow( $title );
  86. } catch ( MalformedTitleException $ex ) {
  87. throw new HttpError( 400, wfMessage( 'pagedata-bad-title', $title ) );
  88. }
  89. $this->httpContentNegotiation( $request, $output, $title, $revision );
  90. }
  91. /**
  92. * Applies HTTP content negotiation.
  93. * If the negotiation is successful, this method will set the appropriate redirect
  94. * in the OutputPage object and return. Otherwise, an HttpError is thrown.
  95. *
  96. * @param WebRequest $request
  97. * @param OutputPage $output
  98. * @param Title $title
  99. * @param int $revision The desired revision
  100. *
  101. * @throws HttpError
  102. */
  103. public function httpContentNegotiation(
  104. WebRequest $request,
  105. OutputPage $output,
  106. Title $title,
  107. $revision = 0
  108. ) {
  109. $contentHandler = ContentHandler::getForTitle( $title );
  110. $mimeTypes = $contentHandler->getSupportedFormats();
  111. $acceptHeader = $request->getHeader( 'Accept' );
  112. if ( $acceptHeader !== false ) {
  113. $parser = new HttpAcceptParser();
  114. $accept = $parser->parseWeights( $acceptHeader );
  115. } else {
  116. // anything goes
  117. $accept = [
  118. '*' => 0.1 // just to make extra sure
  119. ];
  120. // prefer the default
  121. $accept[$mimeTypes[0]] = 1;
  122. }
  123. $negotiator = new HttpAcceptNegotiator( $mimeTypes );
  124. $format = $negotiator->getBestSupportedKey( $accept );
  125. if ( $format === null ) {
  126. $format = isset( $accept['text/html'] ) ? 'text/html' : null;
  127. }
  128. if ( $format === null ) {
  129. $msg = wfMessage( 'pagedata-not-acceptable', implode( ', ', $mimeTypes ) );
  130. throw new HttpError( 406, $msg );
  131. }
  132. $url = $this->getDocUrl( $title, $format, $revision );
  133. $output->redirect( $url, 303 );
  134. }
  135. /**
  136. * Returns a url representing the given title.
  137. *
  138. * @param Title $title
  139. * @param string|null $format The (normalized) format name, or ''
  140. * @param int $revision
  141. * @return string
  142. */
  143. private function getDocUrl( Title $title, $format = '', $revision = 0 ) {
  144. $params = [];
  145. if ( $revision > 0 ) {
  146. $params['oldid'] = $revision;
  147. }
  148. if ( $format === 'text/html' ) {
  149. return $title->getFullURL( $params );
  150. }
  151. $params[ 'action' ] = 'raw';
  152. return $title->getFullURL( $params );
  153. }
  154. }