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
47 public function needsWriteAccess() {
48 return true;
49 }
50
56 abstract protected function getTitleParameter();
57
61 public function validate( Validator $restValidator ) {
62 parent::validate( $restValidator );
63 $this->validateToken( true );
64 }
65
69 protected function mapActionModuleResult( array $data ) {
70 if ( isset( $data['error'] ) ) {
71 throw new LocalizedHttpException( new MessageValue( 'apierror-' . $data['error'] ), 400 );
72 }
73
74 if ( !isset( $data['edit'] ) || !$data['edit']['result'] ) {
75 throw new RuntimeException( 'Bad result structure received from ApiEditPage' );
76 }
77
78 if ( $data['edit']['result'] !== 'Success' ) {
79 // Probably an edit conflict
80 // TODO: which code for null edits?
81 throw new LocalizedHttpException(
82 new MessageValue( "rest-edit-conflict", [ $data['edit']['result'] ] ),
83 409
84 );
85 }
86
87 $title = $this->titleParser->parseTitle( $data['edit']['title'] );
88
89 // This seems wasteful. This is the downside of delegating to the action API module:
90 // if we need additional data in the response, we have to load it.
91 $revision = $this->revisionLookup->getRevisionById( (int)$data['edit']['newrevid'] );
92 $content = $revision->getContent( SlotRecord::MAIN );
93
94 return [
95 'id' => $data['edit']['pageid'],
96 'title' => $this->titleFormatter->getPrefixedText( $title ),
97 'key' => $this->titleFormatter->getPrefixedDBkey( $title ),
98 'latest' => [
99 'id' => $data['edit']['newrevid'],
100 'timestamp' => $data['edit']['newtimestamp'],
101 ],
102 'license' => [
103 'url' => $this->config->get( MainConfigNames::RightsUrl ),
104 'title' => $this->config->get( MainConfigNames::RightsText )
105 ],
106 'content_model' => $data['edit']['contentmodel'],
107 'source' => $content->serialize(),
108 ];
109 }
110
114 protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) {
115 $code = $msg->getApiCode();
116
117 if ( $code === 'protectedpage' ) {
118 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 403 );
119 }
120
121 if ( $code === 'badtoken' ) {
122 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 403 );
123 }
124
125 if ( $code === 'missingtitle' ) {
126 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 404 );
127 }
128
129 if ( $code === 'articleexists' ) {
130 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 409 );
131 }
132
133 if ( $code === 'editconflict' ) {
134 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 409 );
135 }
136
137 if ( $code === 'ratelimited' ) {
138 throw new LocalizedHttpException( MessageValue::newFromSpecifier( $msg ), 429 );
139 }
140
141 // Fall through to generic handling of the error (status 400).
142 parent::throwHttpExceptionForActionModuleError( $msg, $statusCode );
143 }
144
145 protected function mapActionModuleResponse(
146 WebResponse $actionModuleResponse,
147 array $actionModuleResult,
148 Response $response
149 ) {
150 parent::mapActionModuleResponse(
151 $actionModuleResponse,
152 $actionModuleResult,
153 $response
154 );
155
156 if ( $actionModuleResult['edit']['new'] ?? false ) {
157 $response->setStatus( 201 );
158 }
159 }
160
161}
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).
__construct(Config $config, IContentHandlerFactory $contentHandlerFactory, TitleParser $titleParser, TitleFormatter $titleFormatter, RevisionLookup $revisionLookup)
needsWriteAccess()
Indicates whether this route requires write access to the wiki.
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.
Definition Response.php:58
Wrapper for ParamValidator.
Definition Validator.php:37
Value object representing a content slot associated with a page revision.
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:32
Service for looking up page revisions.
A title formatter service for MediaWiki.
A title parser service for MediaWiki.
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).