Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 51 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
EditHandler | |
0.00% |
0 / 51 |
|
0.00% |
0 / 5 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
needsWriteAccess | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getTitleParameter | n/a |
0 / 0 |
n/a |
0 / 0 |
0 | |||||
mapActionModuleResult | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
30 | |||
throwHttpExceptionForActionModuleError | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
56 | |||
mapActionModuleResponse | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Rest\Handler; |
4 | |
5 | use IApiMessage; |
6 | use MediaWiki\Config\Config; |
7 | use MediaWiki\Content\IContentHandlerFactory; |
8 | use MediaWiki\MainConfigNames; |
9 | use MediaWiki\Request\WebResponse; |
10 | use MediaWiki\Rest\HttpException; |
11 | use MediaWiki\Rest\LocalizedHttpException; |
12 | use MediaWiki\Rest\Response; |
13 | use MediaWiki\Rest\TokenAwareHandlerTrait; |
14 | use MediaWiki\Revision\RevisionLookup; |
15 | use MediaWiki\Revision\SlotRecord; |
16 | use MediaWiki\Title\TitleFormatter; |
17 | use MediaWiki\Title\TitleParser; |
18 | use RuntimeException; |
19 | use Wikimedia\Message\MessageValue; |
20 | |
21 | /** |
22 | * Base class for REST API handlers that perform page edits (main slot only). |
23 | */ |
24 | abstract class EditHandler extends ActionModuleBasedHandler { |
25 | use TokenAwareHandlerTrait; |
26 | |
27 | /** @var Config */ |
28 | protected $config; |
29 | |
30 | /** |
31 | * @var IContentHandlerFactory |
32 | */ |
33 | protected $contentHandlerFactory; |
34 | |
35 | /** |
36 | * @var TitleParser |
37 | */ |
38 | protected $titleParser; |
39 | |
40 | /** |
41 | * @var TitleFormatter |
42 | */ |
43 | protected $titleFormatter; |
44 | |
45 | /** |
46 | * @var RevisionLookup |
47 | */ |
48 | protected $revisionLookup; |
49 | |
50 | /** |
51 | * @param Config $config |
52 | * @param IContentHandlerFactory $contentHandlerFactory |
53 | * @param TitleParser $titleParser |
54 | * @param TitleFormatter $titleFormatter |
55 | * @param RevisionLookup $revisionLookup |
56 | */ |
57 | public function __construct( |
58 | Config $config, |
59 | IContentHandlerFactory $contentHandlerFactory, |
60 | TitleParser $titleParser, |
61 | TitleFormatter $titleFormatter, |
62 | RevisionLookup $revisionLookup |
63 | ) { |
64 | $this->config = $config; |
65 | $this->contentHandlerFactory = $contentHandlerFactory; |
66 | $this->titleParser = $titleParser; |
67 | $this->titleFormatter = $titleFormatter; |
68 | $this->revisionLookup = $revisionLookup; |
69 | } |
70 | |
71 | public function needsWriteAccess() { |
72 | return true; |
73 | } |
74 | |
75 | /** |
76 | * Returns the requested title. |
77 | * |
78 | * @return string |
79 | */ |
80 | abstract protected function getTitleParameter(); |
81 | |
82 | /** |
83 | * @inheritDoc |
84 | */ |
85 | protected function mapActionModuleResult( array $data ) { |
86 | if ( isset( $data['error'] ) ) { |
87 | throw new LocalizedHttpException( new MessageValue( 'apierror-' . $data['error'] ), 400 ); |
88 | } |
89 | |
90 | if ( !isset( $data['edit'] ) || !$data['edit']['result'] ) { |
91 | throw new RuntimeException( 'Bad result structure received from ApiEditPage' ); |
92 | } |
93 | |
94 | if ( $data['edit']['result'] !== 'Success' ) { |
95 | // Probably an edit conflict |
96 | // TODO: which code for null edits? |
97 | throw new HttpException( $data['edit']['result'], 409 ); |
98 | } |
99 | |
100 | $title = $this->titleParser->parseTitle( $data['edit']['title'] ); |
101 | |
102 | // This seems wasteful. This is the downside of delegating to the action API module: |
103 | // if we need additional data in the response, we have to load it. |
104 | $revision = $this->revisionLookup->getRevisionById( (int)$data['edit']['newrevid'] ); |
105 | $content = $revision->getContent( SlotRecord::MAIN ); |
106 | |
107 | return [ |
108 | 'id' => $data['edit']['pageid'], |
109 | 'title' => $this->titleFormatter->getPrefixedText( $title ), |
110 | 'key' => $this->titleFormatter->getPrefixedDBkey( $title ), |
111 | 'latest' => [ |
112 | 'id' => $data['edit']['newrevid'], |
113 | 'timestamp' => $data['edit']['newtimestamp'], |
114 | ], |
115 | 'license' => [ |
116 | 'url' => $this->config->get( MainConfigNames::RightsUrl ), |
117 | 'title' => $this->config->get( MainConfigNames::RightsText ) |
118 | ], |
119 | 'content_model' => $data['edit']['contentmodel'], |
120 | 'source' => $content->serialize(), |
121 | ]; |
122 | } |
123 | |
124 | /** |
125 | * @inheritDoc |
126 | */ |
127 | protected function throwHttpExceptionForActionModuleError( IApiMessage $msg, $statusCode = 400 ) { |
128 | $code = $msg->getApiCode(); |
129 | |
130 | if ( $code === 'protectedpage' ) { |
131 | throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 403 ); |
132 | } |
133 | |
134 | if ( $code === 'badtoken' ) { |
135 | throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 403 ); |
136 | } |
137 | |
138 | if ( $code === 'missingtitle' ) { |
139 | throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 404 ); |
140 | } |
141 | |
142 | if ( $code === 'articleexists' ) { |
143 | throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 409 ); |
144 | } |
145 | |
146 | if ( $code === 'editconflict' ) { |
147 | throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 409 ); |
148 | } |
149 | |
150 | if ( $code === 'ratelimited' ) { |
151 | throw new LocalizedHttpException( $this->makeMessageValue( $msg ), 429 ); |
152 | } |
153 | |
154 | // Fall through to generic handling of the error (status 400). |
155 | parent::throwHttpExceptionForActionModuleError( $msg, $statusCode ); |
156 | } |
157 | |
158 | protected function mapActionModuleResponse( |
159 | WebResponse $actionModuleResponse, |
160 | array $actionModuleResult, |
161 | Response $response |
162 | ) { |
163 | parent::mapActionModuleResponse( |
164 | $actionModuleResponse, |
165 | $actionModuleResult, |
166 | $response |
167 | ); |
168 | |
169 | if ( $actionModuleResult['edit']['new'] ?? false ) { |
170 | $response->setStatus( 201 ); |
171 | } |
172 | } |
173 | |
174 | } |