MediaWiki master
EditHandler.php
Go to the documentation of this file.
1<?php
2
4
12use MediaWiki\Rest\TokenAwareHandlerTrait;
18use RuntimeException;
20
24abstract class EditHandler extends ActionModuleBasedHandler {
26
27 protected Config $config;
32
33 public function __construct(
39 ) {
40 $this->config = $config;
41 $this->contentHandlerFactory = $contentHandlerFactory;
42 $this->titleParser = $titleParser;
43 $this->titleFormatter = $titleFormatter;
44 $this->revisionLookup = $revisionLookup;
45 }
46
48 public function needsWriteAccess() {
49 return true;
50 }
51
57 abstract protected function getTitleParameter();
58
62 public function validate( Validator $restValidator ) {
63 parent::validate( $restValidator );
64 $this->validateToken( true );
65 }
66
72 protected function mapActionModuleResult( array $data ) {
73 if ( isset( $data['error'] ) ) {
74 throw new LocalizedHttpException( new MessageValue( 'apierror-' . $data['error'] ), 400 );
75 }
76
77 if ( !isset( $data['edit'] ) || !$data['edit']['result'] ) {
78 throw new RuntimeException( 'Bad result structure received from ApiEditPage' );
79 }
80
81 if ( $data['edit']['result'] !== 'Success' ) {
82 // Probably an edit conflict
83 // TODO: which code for null edits?
84 throw new LocalizedHttpException(
85 new MessageValue( "rest-edit-conflict", [ $data['edit']['result'] ] ),
86 409
87 );
88 }
89
90 $title = $this->titleParser->parseTitle( $data['edit']['title'] );
91
92 // This seems wasteful. This is the downside of delegating to the action API module:
93 // if we need additional data in the response, we have to load it.
94 $revision = $this->revisionLookup->getRevisionById( (int)$data['edit']['newrevid'] );
95 $content = $revision->getContent( SlotRecord::MAIN );
96
97 return [
98 'id' => $data['edit']['pageid'],
99 'title' => $this->titleFormatter->getPrefixedText( $title ),
100 'key' => $this->titleFormatter->getPrefixedDBkey( $title ),
101 'latest' => [
102 'id' => $data['edit']['newrevid'],
103 'timestamp' => $data['edit']['newtimestamp'],
104 ],
105 'license' => [
106 'url' => $this->config->get( MainConfigNames::RightsUrl ),
107 'title' => $this->config->get( MainConfigNames::RightsText )
108 ],
109 'content_model' => $data['edit']['contentmodel'],
110 'source' => $content->serialize(),
111 ];
112 }
113
117 protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) {
118 $code = $msg->getApiCode();
119
120 if ( $code === 'protectedpage' ) {
121 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 403 );
122 }
123
124 if ( $code === 'badtoken' ) {
125 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 403 );
126 }
127
128 if ( $code === 'missingtitle' ) {
129 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 404 );
130 }
131
132 if ( $code === 'articleexists' ) {
133 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 409 );
134 }
135
136 if ( $code === 'editconflict' ) {
137 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 409 );
138 }
139
140 if ( $code === 'ratelimited' ) {
141 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 429 );
142 }
143
144 // Fall through to generic handling of the error (status 400).
145 parent::throwHttpExceptionForActionModuleError( $msg, $statusCode );
146 }
147
148 protected function mapActionModuleResponse(
149 WebResponse $actionModuleResponse,
150 array $actionModuleResult,
151 Response $response
152 ) {
153 parent::mapActionModuleResponse(
154 $actionModuleResponse,
155 $actionModuleResult,
156 $response
157 );
158
159 if ( $actionModuleResult['edit']['new'] ?? false ) {
160 $response->setStatus( 201 );
161 }
162 }
163
164 protected function generateResponseSpec( string $method ): array {
165 $spec = parent::generateResponseSpec( $method );
166
167 $spec['201'][parent::OPENAPI_DESCRIPTION_KEY] = 'OK';
168 $spec['201']['content']['application/json']['schema'] =
169 $spec['200']['content']['application/json']['schema'];
170
171 return $spec;
172 }
173
174}
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()
Allow programs to request this object from WebRequest::response() and handle all outputting (or lack ...
Base class for REST handlers that are implemented by mapping to an existing ApiModule.
Base class for REST API handlers that perform page edits (main slot only).
generateResponseSpec(string $method)
Returns an OpenAPI Responses Object specification structure as an associative array.
__construct(Config $config, IContentHandlerFactory $contentHandlerFactory, TitleParser $titleParser, TitleFormatter $titleFormatter, RevisionLookup $revisionLookup)
needsWriteAccess()
Indicates whether this route requires write access to the wiki.Handlers may override this method to r...
getTitleParameter()
Returns the requested title.
mapActionModuleResult(array $data)
Maps an action API result to a REST API result.mixed Data structure to be converted to JSON and wrapp...
throwHttpExceptionForActionModuleError(IApiMessage $msg, $statusCode=400)
Throws a HttpException for a given IApiMessage that represents an error.Never returns normally....
IContentHandlerFactory $contentHandlerFactory
mapActionModuleResponse(WebResponse $actionModuleResponse, array $actionModuleResult, Response $response)
Transfers relevant information, such as header values, from the WebResponse constructed by the action...
validate(Validator $restValidator)
Validate the request parameters/attributes and body.If there is a validation failure,...
setStatus( $code, $reasonPhrase='')
Set the status code and, optionally, reason phrase.If no reason phrase is specified,...
Definition Response.php:61
Wrapper for ParamValidator.
Definition Validator.php:38
Value object representing a content slot associated with a page revision.
A title formatter service for MediaWiki.
A title parser service for MediaWiki.
Value object representing a message for i18n.
Interface for messages with machine-readable data for use by the API.
getApiCode()
Returns a machine-readable code for use by the API.
Interface for configuration instances.
Definition Config.php:18
Service for looking up page revisions.
Copyright (C) 2011-2020 Wikimedia Foundation and others.
validateToken(bool $allowAnonymousToken=false)
Checks that the given CSRF token is valid (or the used authentication method does not require CSRF).