MediaWiki  master
EditHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Rest\Handler;
4 
5 use Config;
6 use IApiMessage;
14 use TitleFormatter;
15 use TitleParser;
16 use WebResponse;
18 
22 abstract class EditHandler extends ActionModuleBasedHandler {
23 
25  protected $config;
26 
31 
35  protected $titleParser;
36 
40  protected $titleFormatter;
41 
45  protected $revisionLookup;
46 
54  public function __construct(
60  ) {
61  $this->config = $config;
62  $this->contentHandlerFactory = $contentHandlerFactory;
63  $this->titleParser = $titleParser;
64  $this->titleFormatter = $titleFormatter;
65  $this->revisionLookup = $revisionLookup;
66  }
67 
68  public function needsWriteAccess() {
69  return true;
70  }
71 
77  abstract protected function getTitleParameter();
78 
82  protected function mapActionModuleResult( array $data ) {
83  if ( isset( $data['error'] ) ) {
84  throw new LocalizedHttpException( new MessageValue( 'apierror-' . $data['error'] ), 400 );
85  }
86 
87  if ( !isset( $data['edit'] ) || !$data['edit']['result'] ) {
88  throw new HttpException( 'Bad result structure received from ApiEditPage' );
89  }
90 
91  if ( $data['edit']['result'] !== 'Success' ) {
92  // Probably an edit conflict
93  // TODO: which code for null edits?
94  throw new HttpException( $data['edit']['result'], 409 );
95  }
96 
97  $title = $this->titleParser->parseTitle( $data['edit']['title'] );
98 
99  // This seems wasteful. This is the downside of delegating to the action API module:
100  // if we need additional data in the response, we have to load it.
101  $revision = $this->revisionLookup->getRevisionById( (int)$data['edit']['newrevid'] );
102  $content = $revision->getContent( SlotRecord::MAIN );
103 
104  return [
105  'id' => $data['edit']['pageid'],
106  'title' => $this->titleFormatter->getPrefixedText( $title ),
107  'key' => $this->titleFormatter->getPrefixedDBkey( $title ),
108  'latest' => [
109  'id' => $data['edit']['newrevid'],
110  'timestamp' => $data['edit']['newtimestamp'],
111  ],
112  'license' => [
113  'url' => $this->config->get( MainConfigNames::RightsUrl ),
114  'title' => $this->config->get( MainConfigNames::RightsText )
115  ],
116  'content_model' => $data['edit']['contentmodel'],
117  'source' => $content->serialize(),
118  ];
119  }
120 
124  protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) {
125  $code = $msg->getApiCode();
126 
127  if ( $code === 'protectedpage' ) {
128  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 403 );
129  }
130 
131  if ( $code === 'badtoken' ) {
132  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 403 );
133  }
134 
135  if ( $code === 'missingtitle' ) {
136  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 404 );
137  }
138 
139  if ( $code === 'articleexists' ) {
140  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 409 );
141  }
142 
143  if ( $code === 'editconflict' ) {
144  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 409 );
145  }
146 
147  if ( $code === 'ratelimited' ) {
148  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 429 );
149  }
150 
151  // Fall through to generic handling of the error (status 400).
152  parent::throwHttpExceptionForActionModuleError( $msg, $statusCode );
153  }
154 
167  protected function getActionModuleToken() {
168  $body = $this->getValidatedBody();
169 
170  if ( $this->getSession()->getProvider()->safeAgainstCsrf() ) {
171  if ( !empty( $body['token'] ) ) {
172  throw new LocalizedHttpException(
173  new MessageValue( 'rest-extraneous-csrf-token' ),
174  400
175  );
176  }
177 
178  // Since the session is safe against CSRF, just use a known-good token.
179  return $this->getUser()->getEditToken();
180  } else {
181  return $body['token'] ?? '';
182  }
183  }
184 
185  protected function mapActionModuleResponse(
186  WebResponse $actionModuleResponse,
187  array $actionModuleResult,
188  Response $response
189  ) {
190  parent::mapActionModuleResponse(
191  $actionModuleResponse,
192  $actionModuleResult,
193  $response
194  );
195 
196  if ( $actionModuleResult['edit']['new'] ?? false ) {
197  $response->setStatus( 201 );
198  }
199  }
200 
201 }
A class containing constants representing the names of configuration variables.
const RightsText
Name constant for the RightsText setting, for use with Config::get()
const RightsUrl
Name constant for the RightsUrl setting, for use with Config::get()
Base class for REST handlers that are implemented by mapping to an existing ApiModule.
makeMessageValue(IApiMessage $msg)
Constructs a MessageValue from an IApiMessage.
Base class for REST API handlers that perform page edits (main slot only).
Definition: EditHandler.php:22
__construct(Config $config, IContentHandlerFactory $contentHandlerFactory, TitleParser $titleParser, TitleFormatter $titleFormatter, RevisionLookup $revisionLookup)
Definition: EditHandler.php:54
needsWriteAccess()
Indicates whether this route requires write access.
Definition: EditHandler.php:68
getActionModuleToken()
Determines the CSRF token to be passed to the action module.
getTitleParameter()
Returns the requested title.
mapActionModuleResult(array $data)
Maps an action API result to a REST API result.Data structure retrieved from the ApiResult returned b...
Definition: EditHandler.php:82
throwHttpExceptionForActionModuleError(IApiMessage $msg, $statusCode=400)
Throws a HttpException for a given IApiMessage that represents an error.Never returns normally....
IContentHandlerFactory $contentHandlerFactory
Definition: EditHandler.php:30
mapActionModuleResponse(WebResponse $actionModuleResponse, array $actionModuleResult, Response $response)
Transfers relevant information, such as header values, from the WebResponse constructed by the action...
getValidatedBody()
Fetch the validated body.
Definition: Handler.php:294
This is the base exception class for non-fatal exceptions thrown from REST handlers.
setStatus( $code, $reasonPhrase='')
Set the status code and, optionally, reason phrase.
Definition: Response.php:44
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
Definition: WebResponse.php:32
Value object representing a message for i18n.
Interface for configuration instances.
Definition: Config.php:30
Interface for messages with machine-readable data for use by the API.
Definition: IApiMessage.php:39
getApiCode()
Returns a machine-readable code for use by the API.
Service for looking up page revisions.
A title formatter service for MediaWiki.
A title parser service for MediaWiki.
Definition: TitleParser.php:33
$content
Definition: router.php:76