MediaWiki  master
UpdateHandler.php
Go to the documentation of this file.
1 <?php
2 
3 namespace MediaWiki\Rest\Handler;
4 
5 use FormatJson;
6 use IApiMessage;
12 use TextContent;
15 
19 class UpdateHandler extends EditHandler {
20 
25 
29  protected function getTitleParameter() {
30  return $this->getValidatedParams()['title'];
31  }
32 
38  public function setJsonDiffFunction( callable $jsonDiffFunction ) {
39  $this->jsonDiffFunction = $jsonDiffFunction;
40  }
41 
45  public function getParamSettings() {
46  return [
47  'title' => [
48  self::PARAM_SOURCE => 'path',
49  ParamValidator::PARAM_TYPE => 'string',
50  ParamValidator::PARAM_REQUIRED => true,
51  ],
52  ];
53  }
54 
58  public function getBodyValidator( $contentType ) {
59  if ( $contentType !== 'application/json' ) {
60  throw new HttpException( "Unsupported Content-Type",
61  415,
62  [ 'content_type' => $contentType ]
63  );
64  }
65 
66  return new JsonBodyValidator( [
67  'source' => [
68  self::PARAM_SOURCE => 'body',
69  ParamValidator::PARAM_TYPE => 'string',
70  ParamValidator::PARAM_REQUIRED => true,
71  ],
72  'comment' => [
73  self::PARAM_SOURCE => 'body',
74  ParamValidator::PARAM_TYPE => 'string',
75  ParamValidator::PARAM_REQUIRED => true,
76  ],
77  'content_model' => [
78  self::PARAM_SOURCE => 'body',
79  ParamValidator::PARAM_TYPE => 'string',
80  ParamValidator::PARAM_REQUIRED => false,
81  ],
82  'latest' => [
83  self::PARAM_SOURCE => 'body',
84  ParamValidator::PARAM_TYPE => 'array',
85  ParamValidator::PARAM_REQUIRED => false,
86  ],
87  'token' => [
88  self::PARAM_SOURCE => 'body',
89  ParamValidator::PARAM_TYPE => 'string',
90  ParamValidator::PARAM_REQUIRED => false,
91  ParamValidator::PARAM_DEFAULT => '',
92  ],
93  ] );
94  }
95 
99  protected function getActionModuleParameters() {
100  $body = $this->getValidatedBody();
101 
102  $title = $this->getTitleParameter();
103  $baseRevId = $body['latest']['id'] ?? 0;
104 
105  $contentmodel = $body['content_model'] ?: null;
106 
107  if ( $contentmodel !== null && !$this->contentHandlerFactory->isDefinedModel( $contentmodel ) ) {
108  throw new LocalizedHttpException(
109  new MessageValue( 'rest-bad-content-model', [ $contentmodel ] ), 400
110  );
111  }
112 
113  $token = $this->getActionModuleToken();
114 
115  $params = [
116  'action' => 'edit',
117  'title' => $title,
118  'text' => $body['source'],
119  'summary' => $body['comment'],
120  'token' => $token
121  ];
122 
123  if ( $contentmodel !== null ) {
124  $params['contentmodel'] = $contentmodel;
125  }
126 
127  if ( $baseRevId > 0 ) {
128  $params['baserevid'] = $baseRevId;
129  $params['nocreate'] = true;
130  } else {
131  $params['createonly'] = true;
132  }
133 
134  return $params;
135  }
136 
140  protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) {
141  $code = $msg->getApiCode();
142 
143  // Provide a message instructing the client to provide the base revision ID for updates.
144  if ( $code === 'articleexists' ) {
145  $title = $this->getTitleParameter();
146  throw new LocalizedHttpException(
147  new MessageValue( 'rest-update-cannot-create-page', [ $title ] ),
148  409
149  );
150  }
151 
152  if ( $code === 'editconflict' ) {
153  $data = $this->getConflictData();
154  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 409, $data );
155  }
156 
157  parent::throwHttpExceptionForActionModuleError( $msg, $statusCode );
158  }
159 
173  private function getConflictData() {
174  $body = $this->getValidatedBody();
175  $baseRevId = $body['latest']['id'] ?? 0;
176  $title = $this->titleParser->parseTitle( $this->getValidatedParams()['title'] );
177 
178  $baseRev = $this->revisionLookup->getRevisionById( $baseRevId );
179  $currentRev = $this->revisionLookup->getRevisionByTitle( $title );
180 
181  if ( !$baseRev || !$currentRev ) {
182  return [];
183  }
184 
185  $baseContent = $baseRev->getContent(
186  SlotRecord::MAIN,
187  RevisionRecord::FOR_THIS_USER,
188  $this->getUser()
189  );
190  $currentContent = $currentRev->getContent(
191  SlotRecord::MAIN,
192  RevisionRecord::FOR_THIS_USER,
193  $this->getUser()
194  );
195 
196  if ( !$baseContent || !$currentContent ) {
197  return [];
198  }
199 
200  $model = $body['content_model'] ?: $baseContent->getModel();
201  $contentHandler = $this->contentHandlerFactory->getContentHandler( $model );
202  $newContent = $contentHandler->unserializeContent( $body['source'] );
203 
204  if ( !$baseContent instanceof TextContent
205  || !$currentContent instanceof TextContent
206  || !$newContent instanceof TextContent
207  ) {
208  return [];
209  }
210 
211  $localDiff = $this->getDiff( $baseContent, $newContent );
212  $remoteDiff = $this->getDiff( $baseContent, $currentContent );
213 
214  if ( !$localDiff || !$remoteDiff ) {
215  return [];
216  }
217 
218  return [
219  'base' => $baseRev->getId(),
220  'current' => $currentRev->getId(),
221  'local' => $localDiff,
222  'remote' => $remoteDiff,
223  ];
224  }
225 
234  private function getDiff( TextContent $from, TextContent $to ) {
235  if ( !is_callable( $this->jsonDiffFunction ) ) {
236  return null;
237  }
238 
239  $json = ( $this->jsonDiffFunction )( $from->getText(), $to->getText(), 2 );
241  }
242 }
MediaWiki\Rest\Handler
Definition: AbstractContributionHandler.php:3
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
MediaWiki\Rest\Handler\UpdateHandler
Core REST API endpoint that handles page updates (main slot only)
Definition: UpdateHandler.php:19
MediaWiki\Rest\Handler\ActionModuleBasedHandler\makeMessageValue
makeMessageValue(IApiMessage $msg)
Constructs a MessageValue from an IApiMessage.
Definition: ActionModuleBasedHandler.php:250
MediaWiki\Rest\Validator\JsonBodyValidator
Do-nothing body validator.
Definition: JsonBodyValidator.php:14
MediaWiki\Rest\Handler\UpdateHandler\setJsonDiffFunction
setJsonDiffFunction(callable $jsonDiffFunction)
Sets the function to use for JSON diffs, for testing.
Definition: UpdateHandler.php:38
IApiMessage\getApiCode
getApiCode()
Returns a machine-readable code for use by the API.
MediaWiki\Rest\Handler\EditHandler\getActionModuleToken
getActionModuleToken()
Determines the CSRF token to be passed to the action module.
Definition: EditHandler.php:166
IApiMessage
Interface for messages with machine-readable data for use by the API.
Definition: IApiMessage.php:39
MediaWiki\Rest\Handler\UpdateHandler\getBodyValidator
getBodyValidator( $contentType)
Fetch the BodyValidator.Stable to overrideContent type of the request. BodyValidator
Definition: UpdateHandler.php:58
TextContent\getText
getText()
Returns the text represented by this Content object, as a string.
Definition: TextContent.php:153
Wikimedia\Message\MessageValue
Value object representing a message for i18n.
Definition: MessageValue.php:16
FormatJson\decode
static decode( $value, $assoc=false)
Decodes a JSON string.
Definition: FormatJson.php:174
FormatJson
JSON formatter wrapper class.
Definition: FormatJson.php:26
MediaWiki\Rest\Handler\UpdateHandler\$jsonDiffFunction
callable $jsonDiffFunction
Definition: UpdateHandler.php:24
FormatJson\FORCE_ASSOC
const FORCE_ASSOC
If set, treat JSON objects '{...}' as associative arrays.
Definition: FormatJson.php:63
MediaWiki\Rest\Handler\getValidatedBody
getValidatedBody()
Fetch the validated body.
Definition: Handler.php:269
MediaWiki\Rest\Handler\UpdateHandler\getConflictData
getConflictData()
Returns an associative array to be used in the response in the event of edit conflicts.
Definition: UpdateHandler.php:173
$title
$title
Definition: testCompression.php:38
MediaWiki\Rest\Handler\getValidatedParams
getValidatedParams()
Fetch the validated parameters.
Definition: Handler.php:257
MediaWiki\Rest\Handler\EditHandler
Base class for REST API handlers that perform page edits (main slot only).
Definition: EditHandler.php:21
MediaWiki\Rest\Handler\ActionModuleBasedHandler\getUser
getUser()
Definition: ActionModuleBasedHandler.php:41
MediaWiki\Rest\Handler\UpdateHandler\getTitleParameter
getTitleParameter()
Returns the requested title.string
Definition: UpdateHandler.php:29
MediaWiki\Rest\HttpException
This is the base exception class for non-fatal exceptions thrown from REST handlers.
Definition: HttpException.php:12
MediaWiki\Rest\Handler\UpdateHandler\getDiff
getDiff(TextContent $from, TextContent $to)
Returns a text diff encoded as an array, to be included in the response data.
Definition: UpdateHandler.php:234
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:39
MediaWiki\Rest\Handler\UpdateHandler\throwHttpExceptionForActionModuleError
throwHttpExceptionForActionModuleError(IApiMessage $msg, $statusCode=400)
Throws a HttpException for a given IApiMessage that represents an error.Never returns normally....
Definition: UpdateHandler.php:140
MediaWiki\Rest\Handler\UpdateHandler\getParamSettings
getParamSettings()
Fetch ParamValidator settings for parameters.Every setting must include self::PARAM_SOURCE to specify...
Definition: UpdateHandler.php:45
MediaWiki\Rest\Handler\UpdateHandler\getActionModuleParameters
getActionModuleParameters()
Maps a REST API request to an action API request.Implementations typically use information returned b...
Definition: UpdateHandler.php:99
Wikimedia\ParamValidator\ParamValidator
Service for formatting and validating API parameters.
Definition: ParamValidator.php:42
MediaWiki\Rest\LocalizedHttpException
@newable
Definition: LocalizedHttpException.php:10
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40