MediaWiki  master
ApiStashEdit.php
Go to the documentation of this file.
1 <?php
24 
38 class ApiStashEdit extends ApiBase {
39  public function execute() {
40  $user = $this->getUser();
41  $params = $this->extractRequestParams();
42 
43  if ( $user->isBot() ) { // sanity
44  $this->dieWithError( 'apierror-botsnotsupported' );
45  }
46 
47  $editStash = MediaWikiServices::getInstance()->getPageEditStash();
48  $page = $this->getTitleOrPageId( $params );
49  $title = $page->getTitle();
50 
51  if ( !$this->getContentHandlerFactory()
52  ->getContentHandler( $params['contentmodel'] )
53  ->isSupportedFormat( $params['contentformat'] )
54  ) {
55  $this->dieWithError(
56  [ 'apierror-badformat-generic', $params['contentformat'], $params['contentmodel'] ],
57  'badmodelformat'
58  );
59  }
60 
61  $this->requireOnlyOneParameter( $params, 'stashedtexthash', 'text' );
62 
63  $text = null;
64  $textHash = null;
65  if ( $params['stashedtexthash'] !== null ) {
66  // Load from cache since the client indicates the text is the same as last stash
67  $textHash = $params['stashedtexthash'];
68  if ( !preg_match( '/^[0-9a-f]{40}$/', $textHash ) ) {
69  $this->dieWithError( 'apierror-stashedit-missingtext', 'missingtext' );
70  }
71  $text = $editStash->fetchInputText( $textHash );
72  if ( !is_string( $text ) ) {
73  $this->dieWithError( 'apierror-stashedit-missingtext', 'missingtext' );
74  }
75  } else {
76  // 'text' was passed. Trim and fix newlines so the key SHA1's
77  // match (see WebRequest::getText())
78  $text = rtrim( str_replace( "\r\n", "\n", $params['text'] ) );
79  $textHash = sha1( $text );
80  }
81 
82  $textContent = ContentHandler::makeContent(
83  $text, $title, $params['contentmodel'], $params['contentformat'] );
84 
85  $page = WikiPage::factory( $title );
86  if ( $page->exists() ) {
87  // Page exists: get the merged content with the proposed change
88  $baseRev = MediaWikiServices::getInstance()
89  ->getRevisionLookup()
90  ->getRevisionByPageId( $page->getId(), $params['baserevid'] );
91  if ( !$baseRev ) {
92  $this->dieWithError( [ 'apierror-nosuchrevid', $params['baserevid'] ] );
93  }
94  $currentRev = $page->getRevisionRecord();
95  if ( !$currentRev ) {
96  $this->dieWithError( [ 'apierror-missingrev-pageid', $page->getId() ], 'missingrev' );
97  }
98  // Merge in the new version of the section to get the proposed version
99  $editContent = $page->replaceSectionAtRev(
100  $params['section'],
101  $textContent,
102  $params['sectiontitle'],
103  $baseRev->getId()
104  );
105  if ( !$editContent ) {
106  $this->dieWithError( 'apierror-sectionreplacefailed', 'replacefailed' );
107  }
108  if ( $currentRev->getId() == $baseRev->getId() ) {
109  // Base revision was still the latest; nothing to merge
110  $content = $editContent;
111  } else {
112  // Merge the edit into the current version
113  $baseContent = $baseRev->getContent( SlotRecord::MAIN );
114  $currentContent = $currentRev->getContent( SlotRecord::MAIN );
115  if ( !$baseContent || !$currentContent ) {
116  $this->dieWithError( [ 'apierror-missingcontent-pageid', $page->getId() ], 'missingrev' );
117  }
119  ->getContentHandler( $baseContent->getModel() )
120  ->merge3( $baseContent, $editContent, $currentContent );
121  }
122  } else {
123  // New pages: use the user-provided content model
124  $content = $textContent;
125  }
126 
127  if ( !$content ) { // merge3() failed
128  $this->getResult()->addValue( null,
129  $this->getModuleName(), [ 'status' => 'editconflict' ] );
130  return;
131  }
132 
133  if ( $user->pingLimiter( 'stashedit' ) ) {
134  $status = 'ratelimited';
135  } else {
136  $status = $editStash->parseAndCache( $page, $content, $user, $params['summary'] );
137  $editStash->stashInputText( $text, $textHash );
138  }
139 
140  $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
141  $stats->increment( "editstash.cache_stores.$status" );
142 
143  $ret = [ 'status' => $status ];
144  // If we were rate-limited, we still return the pre-existing valid hash if one was passed
145  if ( $status !== 'ratelimited' || $params['stashedtexthash'] !== null ) {
146  $ret['texthash'] = $textHash;
147  }
148 
149  $this->getResult()->addValue( null, $this->getModuleName(), $ret );
150  }
151 
161  public function parseAndStash( WikiPage $page, Content $content, User $user, $summary ) {
162  $editStash = MediaWikiServices::getInstance()->getPageEditStash();
163 
164  return $editStash->parseAndCache( $page, $content, $user, $summary ?? '' );
165  }
166 
167  public function getAllowedParams() {
168  return [
169  'title' => [
170  ApiBase::PARAM_TYPE => 'string',
172  ],
173  'section' => [
174  ApiBase::PARAM_TYPE => 'string',
175  ],
176  'sectiontitle' => [
177  ApiBase::PARAM_TYPE => 'string'
178  ],
179  'text' => [
180  ApiBase::PARAM_TYPE => 'text',
181  ApiBase::PARAM_DFLT => null
182  ],
183  'stashedtexthash' => [
184  ApiBase::PARAM_TYPE => 'string',
185  ApiBase::PARAM_DFLT => null
186  ],
187  'summary' => [
188  ApiBase::PARAM_TYPE => 'string',
189  ApiBase::PARAM_DFLT => ''
190  ],
191  'contentmodel' => [
192  ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getContentModels(),
194  ],
195  'contentformat' => [
196  ApiBase::PARAM_TYPE => $this->getContentHandlerFactory()->getAllContentFormats(),
198  ],
199  'baserevid' => [
200  ApiBase::PARAM_TYPE => 'integer',
202  ]
203  ];
204  }
205 
206  public function needsToken() {
207  return 'csrf';
208  }
209 
210  public function mustBePosted() {
211  return true;
212  }
213 
214  public function isWriteMode() {
215  return true;
216  }
217 
218  public function isInternal() {
219  return true;
220  }
221 
223  return MediaWikiServices::getInstance()->getContentHandlerFactory();
224  }
225 }
ApiStashEdit\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiStashEdit.php:206
ApiBase\PARAM_REQUIRED
const PARAM_REQUIRED
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:77
ApiStashEdit\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiStashEdit.php:214
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:154
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1382
true
return true
Definition: router.php:90
ApiBase\getTitleOrPageId
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition: ApiBase.php:986
ApiBase\PARAM_TYPE
const PARAM_TYPE
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:71
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:565
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:52
ApiStashEdit\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiStashEdit.php:39
ContextSource\getUser
getUser()
Stable to override.
Definition: ContextSource.php:131
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:52
WikiPage\factory
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
Definition: WikiPage.php:156
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:717
$title
$title
Definition: testCompression.php:38
ApiStashEdit\getContentHandlerFactory
getContentHandlerFactory()
Definition: ApiStashEdit.php:222
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:142
ApiStashEdit\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiStashEdit.php:167
$content
$content
Definition: router.php:76
ApiBase\requireOnlyOneParameter
requireOnlyOneParameter( $params,... $required)
Die if none or more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:854
MediaWiki\Content\IContentHandlerFactory
Definition: IContentHandlerFactory.php:10
ApiStashEdit\isInternal
isInternal()
Indicates whether this module is "internal" Internal API modules are not (yet) intended for 3rd party...
Definition: ApiStashEdit.php:218
ApiStashEdit\parseAndStash
parseAndStash(WikiPage $page, Content $content, User $user, $summary)
Definition: ApiStashEdit.php:161
Content
Base interface for content objects.
Definition: Content.php:35
ApiBase\PARAM_DFLT
const PARAM_DFLT
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:69
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:444
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:56
ApiStashEdit
Prepare an edit in shared cache so that it can be reused on edit.
Definition: ApiStashEdit.php:38
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
ApiStashEdit\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request Stable to override.
Definition: ApiStashEdit.php:210