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 MWTimestamp;
13 use TextContent;
16 
20 class UpdateHandler extends EditHandler {
21 
26 
30  protected function getTitleParameter() {
31  return $this->getValidatedParams()['title'];
32  }
33 
39  public function setJsonDiffFunction( callable $jsonDiffFunction ) {
40  $this->jsonDiffFunction = $jsonDiffFunction;
41  }
42 
46  public function getParamSettings() {
47  return [
48  'title' => [
49  self::PARAM_SOURCE => 'path',
50  ParamValidator::PARAM_TYPE => 'string',
51  ParamValidator::PARAM_REQUIRED => true,
52  ],
53  ];
54  }
55 
59  public function getBodyValidator( $contentType ) {
60  if ( $contentType !== 'application/json' ) {
61  throw new HttpException( "Unsupported Content-Type",
62  415,
63  [ 'content_type' => $contentType ]
64  );
65  }
66 
67  return new JsonBodyValidator( [
68  'source' => [
69  self::PARAM_SOURCE => 'body',
70  ParamValidator::PARAM_TYPE => 'string',
71  ParamValidator::PARAM_REQUIRED => true,
72  ],
73  'comment' => [
74  self::PARAM_SOURCE => 'body',
75  ParamValidator::PARAM_TYPE => 'string',
76  ParamValidator::PARAM_REQUIRED => true,
77  ],
78  'content_model' => [
79  self::PARAM_SOURCE => 'body',
80  ParamValidator::PARAM_TYPE => 'string',
81  ParamValidator::PARAM_REQUIRED => false,
82  ],
83  'latest' => [
84  self::PARAM_SOURCE => 'body',
85  ParamValidator::PARAM_TYPE => 'array',
86  ParamValidator::PARAM_REQUIRED => false,
87  ],
88  'token' => [
89  self::PARAM_SOURCE => 'body',
90  ParamValidator::PARAM_TYPE => 'string',
91  ParamValidator::PARAM_REQUIRED => false,
92  ParamValidator::PARAM_DEFAULT => '',
93  ],
94  ] );
95  }
96 
100  protected function getActionModuleParameters() {
101  $body = $this->getValidatedBody();
102 
103  $title = $this->getTitleParameter();
104  $baseRevId = $body['latest']['id'] ?? 0;
105 
106  $contentmodel = $body['content_model'] ?: null;
107 
108  if ( $contentmodel !== null && !$this->contentHandlerFactory->isDefinedModel( $contentmodel ) ) {
109  throw new LocalizedHttpException(
110  new MessageValue( 'rest-bad-content-model', [ $contentmodel ] ), 400
111  );
112  }
113 
114  $token = $this->getActionModuleToken();
115 
116  $params = [
117  'action' => 'edit',
118  'title' => $title,
119  'text' => $body['source'],
120  'summary' => $body['comment'],
121  'token' => $token
122  ];
123 
124  if ( $contentmodel !== null ) {
125  $params['contentmodel'] = $contentmodel;
126  }
127 
128  if ( $baseRevId > 0 ) {
129  $params['baserevid'] = $baseRevId;
130  $params['nocreate'] = true;
131  } else {
132  $params['createonly'] = true;
133  }
134 
135  return $params;
136  }
137 
141  protected function mapActionModuleResult( array $data ) {
142  if ( isset( $data['edit']['nochange'] ) ) {
143  // Null-edit, no new revision was created. The new revision is the same as the old.
144  // We may want to signal this more explicitly to the client in the future.
145 
146  $title = $this->titleParser->parseTitle( $this->getValidatedParams()['title'] );
147  $currentRev = $this->revisionLookup->getRevisionByTitle( $title );
148 
149  $data['edit']['newrevid'] = $currentRev->getId();
150  $data['edit']['newtimestamp']
151  = MWTimestamp::convert( TS_ISO_8601, $currentRev->getTimestamp() );
152  }
153 
154  return parent::mapActionModuleResult( $data );
155  }
156 
160  protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) {
161  $code = $msg->getApiCode();
162 
163  // Provide a message instructing the client to provide the base revision ID for updates.
164  if ( $code === 'articleexists' ) {
165  $title = $this->getTitleParameter();
166  throw new LocalizedHttpException(
167  new MessageValue( 'rest-update-cannot-create-page', [ $title ] ),
168  409
169  );
170  }
171 
172  if ( $code === 'editconflict' ) {
173  $data = $this->getConflictData();
174  throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 409, $data );
175  }
176 
177  parent::throwHttpExceptionForActionModuleError( $msg, $statusCode );
178  }
179 
193  private function getConflictData() {
194  $body = $this->getValidatedBody();
195  $baseRevId = $body['latest']['id'] ?? 0;
196  $title = $this->titleParser->parseTitle( $this->getValidatedParams()['title'] );
197 
198  $baseRev = $this->revisionLookup->getRevisionById( $baseRevId );
199  $currentRev = $this->revisionLookup->getRevisionByTitle( $title );
200 
201  if ( !$baseRev || !$currentRev ) {
202  return [];
203  }
204 
205  $baseContent = $baseRev->getContent(
206  SlotRecord::MAIN,
208  $this->getAuthority()
209  );
210  $currentContent = $currentRev->getContent(
211  SlotRecord::MAIN,
213  $this->getAuthority()
214  );
215 
216  if ( !$baseContent || !$currentContent ) {
217  return [];
218  }
219 
220  $model = $body['content_model'] ?: $baseContent->getModel();
221  $contentHandler = $this->contentHandlerFactory->getContentHandler( $model );
222  $newContent = $contentHandler->unserializeContent( $body['source'] );
223 
224  if ( !$baseContent instanceof TextContent
225  || !$currentContent instanceof TextContent
226  || !$newContent instanceof TextContent
227  ) {
228  return [];
229  }
230 
231  $localDiff = $this->getDiff( $baseContent, $newContent );
232  $remoteDiff = $this->getDiff( $baseContent, $currentContent );
233 
234  if ( !$localDiff || !$remoteDiff ) {
235  return [];
236  }
237 
238  return [
239  'base' => $baseRev->getId(),
240  'current' => $currentRev->getId(),
241  'local' => $localDiff,
242  'remote' => $remoteDiff,
243  ];
244  }
245 
254  private function getDiff( TextContent $from, TextContent $to ) {
255  if ( !is_callable( $this->jsonDiffFunction ) ) {
256  return null;
257  }
258 
259  $json = ( $this->jsonDiffFunction )( $from->getText(), $to->getText(), 2 );
261  }
262 }
MWTimestamp
Library for creating and parsing MW-style timestamps.
Definition: MWTimestamp.php:38
MediaWiki\Rest\Handler
Definition: AbstractContributionHandler.php:3
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
MediaWiki\Rest\Handler\UpdateHandler
Core REST API endpoint that handles page updates (main slot only)
Definition: UpdateHandler.php:20
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:39
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.Stability: stableto overrideContent type of the request. BodyValidator
Definition: UpdateHandler.php:59
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:146
FormatJson
JSON formatter wrapper class.
Definition: FormatJson.php:26
MediaWiki\Rest\Handler\UpdateHandler\$jsonDiffFunction
callable $jsonDiffFunction
Definition: UpdateHandler.php:25
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:294
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:193
$title
$title
Definition: testCompression.php:38
MediaWiki\Rest\Handler\getValidatedParams
getValidatedParams()
Fetch the validated parameters.
Definition: Handler.php:282
MediaWiki\Rest\Handler\EditHandler
Base class for REST API handlers that perform page edits (main slot only).
Definition: EditHandler.php:21
MediaWiki\Rest\Handler\UpdateHandler\getTitleParameter
getTitleParameter()
Returns the requested title.string
Definition: UpdateHandler.php:30
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:254
MediaWiki\Rest\Handler\UpdateHandler\mapActionModuleResult
mapActionModuleResult(array $data)
Maps an action API result to a REST API result.Data structure retrieved from the ApiResult returned b...
Definition: UpdateHandler.php:141
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:160
MediaWiki\Rest\Handler\UpdateHandler\getParamSettings
getParamSettings()
Fetch ParamValidator settings for parameters.Every setting must include self::PARAM_SOURCE to specify...
Definition: UpdateHandler.php:46
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:100
MediaWiki\Revision\RevisionRecord\FOR_THIS_USER
const FOR_THIS_USER
Definition: RevisionRecord.php:63
MediaWiki\Rest\Handler\getAuthority
getAuthority()
Get the current acting authority.
Definition: Handler.php:148
Wikimedia\ParamValidator\ParamValidator
Service for formatting and validating API parameters.
Definition: ParamValidator.php:42
MediaWiki\Rest\LocalizedHttpException
@newable
Definition: LocalizedHttpException.php:10
MediaWiki\Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40