45 $titleObj = $pageObj->getTitle();
48 if ( $params[
'redirect'] ) {
49 if ( $params[
'prependtext'] ===
null && $params[
'appendtext'] ===
null
50 && $params[
'section'] !==
'new'
54 if ( $titleObj->isRedirect() ) {
55 $oldTitle = $titleObj;
57 $titles = Revision::newFromTitle( $oldTitle,
false, Revision::READ_LATEST )
58 ->getContent( RevisionRecord::FOR_THIS_USER, $user )
65 foreach ( $titles as $id => $newTitle ) {
66 $titles[$id - 1] = $titles[$id - 1] ?? $oldTitle;
69 'from' => $titles[$id - 1]->getPrefixedText(),
70 'to' => $newTitle->getPrefixedText()
73 $titleObj = $newTitle;
76 if ( $titleObj->isExternal() || !$titleObj->canExist() ) {
77 $redirValues[count( $redirValues ) - 1][
'to'] = $titleObj->getFullText();
80 'apierror-edit-invalidredirect',
84 'edit-invalidredirect',
85 [
'redirects' => $redirValues ]
90 ApiResult::setIndexedTagName( $redirValues,
'r' );
91 $apiResult->addValue(
null,
'redirects', $redirValues );
94 $pageObj = WikiPage::factory( $titleObj );
98 if ( !isset( $params[
'contentmodel'] ) || $params[
'contentmodel'] ==
'' ) {
99 $contentHandler = $pageObj->getContentHandler();
101 $contentHandler = ContentHandler::getForModelID( $params[
'contentmodel'] );
103 $contentModel = $contentHandler->getModelID();
105 $name = $titleObj->getPrefixedDBkey();
106 $model = $contentHandler->getModelID();
108 if ( $params[
'undo'] > 0 ) {
110 } elseif ( $contentHandler->supportsDirectApiEditing() ===
false ) {
111 $this->
dieWithError( [
'apierror-no-direct-editing', $model, $name ] );
114 if ( !isset( $params[
'contentformat'] ) || $params[
'contentformat'] ==
'' ) {
115 $contentFormat = $contentHandler->getDefaultFormat();
117 $contentFormat = $params[
'contentformat'];
120 if ( !$contentHandler->isSupportedFormat( $contentFormat ) ) {
121 $this->
dieWithError( [
'apierror-badformat', $contentFormat, $model, $name ] );
124 if ( $params[
'createonly'] && $titleObj->exists() ) {
127 if ( $params[
'nocreate'] && !$titleObj->exists() ) {
134 $titleObj->exists() ?
'edit' : [
'edit',
'create' ],
135 [
'autoblock' =>
true ]
138 $toMD5 = $params[
'text'];
139 if ( !is_null( $params[
'appendtext'] ) || !is_null( $params[
'prependtext'] ) ) {
144 # If this is a MediaWiki:x message, then load the messages
145 # and return the message value for x.
146 $text = $titleObj->getDefaultMessageText();
147 if ( $text ===
false ) {
152 $content = ContentHandler::makeContent( $text, $titleObj );
155 'wrap' =>
ApiMessage::create(
'apierror-contentserializationexception',
'parseerror' )
160 # Otherwise, make a new empty content.
161 $content = $contentHandler->makeEmptyContent();
168 $modelName = $contentHandler->getModelID();
169 $this->
dieWithError( [
'apierror-appendnotsupported', $modelName ] );
172 if ( !is_null( $params[
'section'] ) ) {
173 if ( !$contentHandler->supportsSections() ) {
174 $modelName = $contentHandler->getModelID();
175 $this->
dieWithError( [
'apierror-sectionsnotsupported', $modelName ] );
178 if ( $params[
'section'] ==
'new' ) {
183 $section = $params[
'section'];
195 $text =
$content->serialize( $contentFormat );
198 $params[
'text'] = $params[
'prependtext'] . $text . $params[
'appendtext'];
199 $toMD5 = $params[
'prependtext'] . $params[
'appendtext'];
202 if ( $params[
'undo'] > 0 ) {
203 if ( $params[
'undoafter'] > 0 ) {
204 if ( $params[
'undo'] < $params[
'undoafter'] ) {
205 list( $params[
'undo'], $params[
'undoafter'] ) =
206 [ $params[
'undoafter'], $params[
'undo'] ];
208 $undoafterRev = Revision::newFromId( $params[
'undoafter'] );
210 $undoRev = Revision::newFromId( $params[
'undo'] );
211 if ( is_null( $undoRev ) || $undoRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
212 $this->
dieWithError( [
'apierror-nosuchrevid', $params[
'undo'] ] );
215 if ( $params[
'undoafter'] == 0 ) {
216 $undoafterRev = $undoRev->getPrevious();
218 if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
219 $this->
dieWithError( [
'apierror-nosuchrevid', $params[
'undoafter'] ] );
222 if ( $undoRev->getPage() != $pageObj->getId() ) {
223 $this->
dieWithError( [
'apierror-revwrongpage', $undoRev->getId(),
224 $titleObj->getPrefixedText() ] );
226 if ( $undoafterRev->getPage() != $pageObj->getId() ) {
227 $this->
dieWithError( [
'apierror-revwrongpage', $undoafterRev->getId(),
228 $titleObj->getPrefixedText() ] );
231 $newContent = $contentHandler->getUndoContent(
232 $pageObj->getRevision(),
237 if ( !$newContent ) {
240 if ( empty( $params[
'contentmodel'] )
241 && empty( $params[
'contentformat'] )
248 if ( !$newContent->isSupportedFormat( $contentFormat ) ) {
249 $contentFormat = $undoafterRev->getContentFormat();
252 $contentModel = $newContent->getModel();
254 $params[
'text'] = $newContent->serialize( $contentFormat );
258 if ( is_null( $params[
'summary'] ) ) {
259 $nextRev = MediaWikiServices::getInstance()->getRevisionLookup()
260 ->getNextRevision( $undoafterRev->getRevisionRecord() );
261 if ( $nextRev && $nextRev->getId() == $params[
'undo'] ) {
262 $params[
'summary'] =
wfMessage(
'undo-summary' )
263 ->params( $params[
'undo'], $undoRev->getUserText() )
264 ->inContentLanguage()->text();
270 if ( !is_null( $params[
'md5'] ) && md5( $toMD5 ) !== $params[
'md5'] ) {
277 'wpTextbox1' => $params[
'text'],
278 'format' => $contentFormat,
279 'model' => $contentModel,
280 'wpEditToken' => $params[
'token'],
281 'wpIgnoreBlankSummary' =>
true,
282 'wpIgnoreBlankArticle' =>
true,
283 'wpIgnoreSelfRedirect' =>
true,
284 'bot' => $params[
'bot'],
288 if ( !is_null( $params[
'summary'] ) ) {
289 $requestArray[
'wpSummary'] = $params[
'summary'];
292 if ( !is_null( $params[
'sectiontitle'] ) ) {
293 $requestArray[
'wpSectionTitle'] = $params[
'sectiontitle'];
297 if ( $params[
'undo'] > 0 ) {
298 $requestArray[
'wpUndidRevision'] = $params[
'undo'];
303 if ( $params[
'basetimestamp'] !==
null && (
bool)$this->
getMain()->getVal(
'basetimestamp' ) ) {
304 $requestArray[
'wpEdittime'] = $params[
'basetimestamp'];
306 $requestArray[
'wpEdittime'] = $pageObj->getTimestamp();
309 if ( $params[
'starttimestamp'] !==
null ) {
310 $requestArray[
'wpStarttime'] = $params[
'starttimestamp'];
315 if ( $params[
'minor'] || ( !$params[
'notminor'] && $user->getOption(
'minordefault' ) ) ) {
316 $requestArray[
'wpMinoredit'] =
'';
319 if ( $params[
'recreate'] ) {
320 $requestArray[
'wpRecreate'] =
'';
323 if ( !is_null( $params[
'section'] ) ) {
324 $section = $params[
'section'];
325 if ( !preg_match(
'/^((T-)?\d+|new)$/', $section ) ) {
329 if ( $section !==
'0' && $section !=
'new'
332 $this->
dieWithError( [
'apierror-nosuchsection', $section ] );
334 $requestArray[
'wpSection'] = $params[
'section'];
336 $requestArray[
'wpSection'] =
'';
342 if ( $params[
'watch'] ) {
344 } elseif ( $params[
'unwatch'] ) {
349 $requestArray[
'wpWatchthis'] =
'';
353 if ( $params[
'tags'] ) {
355 if ( $tagStatus->isOK() ) {
356 $requestArray[
'wpChangeTags'] = implode(
',', $params[
'tags'] );
364 $requestArray += $this->
getRequest()->getValues();
376 $articleContext->setWikiPage( $pageObj );
377 $articleContext->setUser( $this->
getUser() );
382 $ep =
new EditPage( $articleObject );
384 $ep->setApiEditOverride(
true );
385 $ep->setContextTitle( $titleObj );
386 $ep->importFormData( $req );
390 $oldRevId = $articleObject->getRevIdFetched();
397 $status = $ep->attemptSave( $result );
401 switch ( $status->value ) {
404 if ( isset( $status->apiHookResult ) ) {
405 $r = $status->apiHookResult;
406 $r[
'result'] =
'Failure';
410 if ( !$status->getErrors() ) {
413 $status->fatal(
'hookaborted' );
434 $r[
'result'] =
'Success';
435 $r[
'pageid'] = (int)$titleObj->getArticleID();
436 $r[
'title'] = $titleObj->getPrefixedText();
437 $r[
'contentmodel'] = $articleObject->getContentModel();
438 $newRevId = $articleObject->getLatest();
439 if ( $newRevId == $oldRevId ) {
440 $r[
'nochange'] =
true;
442 $r[
'oldrevid'] = (int)$oldRevId;
443 $r[
'newrevid'] = (int)$newRevId;
445 $pageObj->getTimestamp() );
450 if ( !$status->getErrors() ) {
453 switch ( $status->value ) {
456 $status->fatal(
'apierror-noimageredirect-anon' );
459 $status->fatal(
'apierror-noimageredirect' );
463 $status->fatal(
'apierror-contenttoobig', $this->
getConfig()->
get(
'MaxArticleSize' ) );
466 $status->fatal(
'apierror-noedit-anon' );
469 $status->fatal(
'apierror-cantchangecontentmodel' );
472 $status->fatal(
'apierror-pagedeleted' );
475 $status->fatal(
'editconflict' );
483 $status->fatal(
'apierror-spamdetected', $result[
'spam'] );
486 $status->fatal(
'apierror-noedit' );
489 $status->fatal(
'apierror-ratelimited' );
492 $status->fatal(
'nocreate-loggedin' );
495 $status->fatal(
'apierror-emptypage' );
498 $status->fatal(
'apierror-emptynewsection' );
501 $status->fatal(
'apierror-summaryrequired' );
504 wfWarn( __METHOD__ .
": Unknown EditPage code {$status->value} with no message" );
505 $status->fatal(
'apierror-unknownerror-editpage', $status->value );
549 'starttimestamp' => [
553 'createonly' =>
false,
612 'action=edit&title=Test&summary=test%20summary&' .
613 'text=article%20content&basetimestamp=2007-08-24T12:34:54Z&token=123ABC'
614 =>
'apihelp-edit-example-edit',
615 'action=edit&title=Test&summary=NOTOC&minor=&' .
616 'prependtext=__NOTOC__%0A&basetimestamp=2007-08-24T12:34:54Z&token=123ABC'
617 =>
'apihelp-edit-example-prepend',
618 'action=edit&title=Test&undo=13585&undoafter=13579&' .
619 'basetimestamp=2007-08-24T12:34:54Z&token=123ABC'
620 =>
'apihelp-edit-example-undo',
625 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Edit';
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(! $wgDBerrorLogTZ) $wgRequest
if(! $wgRequest->checkUrlExtension()) if(isset( $_SERVER['PATH_INFO']) && $_SERVER['PATH_INFO'] !='') $wgTitle
This abstract class implements many basic API functions, and is the base of all API classes.
const PARAM_DEPRECATED
(boolean) Is the parameter deprecated (will show a warning)?
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
checkTitleUserPermissions(LinkTarget $linkTarget, $actions, $options=[])
Helper function for permission-denied errors.
getMain()
Get the main module.
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
dieReadOnly()
Helper function for readonly errors.
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
dieBlocked(AbstractBlock $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
getResult()
Get the result object.
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
const PARAM_RANGE_ENFORCE
(boolean) For PARAM_TYPE 'integer', enforce PARAM_MIN and PARAM_MAX?
getWatchlistValue( $watchlist, $titleObj, $userOption=null)
Return true if we're to watch the page, false if not, null if no change.
dieWithException( $exception, array $options=[])
Abort execution with an error derived from an exception.
requireAtLeastOneParameter( $params, $required)
Die if none of a certain set of parameters is set and not false.
getModuleName()
Get the name of the module being executed by this instance.
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
A module that allows for editing and creating pages.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
needsToken()
Returns the token type this module requires in order to execute.
isWriteMode()
Indicates whether this module requires write mode.
mustBePosted()
Indicates whether this module must be called with a POST request.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
getExamplesMessages()
Returns usage examples for this module.
getHelpUrls()
Return links to more detailed help pages about the module.
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Similar to FauxRequest, but only fakes URL parameters and method (POST or GET) and use the base reque...
The edit page/HTML interface (split from Article) The actual database and text munging is still in Ar...
const AS_HOOK_ERROR_EXPECTED
Status: A hook function returned an error.
const AS_READ_ONLY_PAGE_ANON
Status: this anonymous user is not allowed to edit this page.
const AS_MAX_ARTICLE_SIZE_EXCEEDED
Status: article is too big (> $wgMaxArticleSize), after merging in the new section.
const AS_CONFLICT_DETECTED
Status: (non-resolvable) edit conflict.
const AS_SPAM_ERROR
Status: summary contained spam according to one of the regexes in $wgSummarySpamRegex.
const AS_READ_ONLY_PAGE_LOGGED
Status: this logged in user is not allowed to edit this page.
const AS_BLANK_ARTICLE
Status: user tried to create a blank page and wpIgnoreBlankArticle == false.
const AS_READ_ONLY_PAGE
Status: wiki is in readonly mode (wfReadOnly() == true)
const AS_CONTENT_TOO_BIG
Status: Content too big (> $wgMaxArticleSize)
const AS_BLOCKED_PAGE_FOR_USER
Status: User is blocked from editing this page.
const AS_NO_CHANGE_CONTENT_MODEL
Status: user tried to modify the content model, but is not allowed to do that ( User::isAllowed('edit...
const AS_IMAGE_REDIRECT_LOGGED
Status: logged in user is not allowed to upload (User::isAllowed('upload') == false)
const AS_ARTICLE_WAS_DELETED
Status: article was deleted while editing and param wpRecreate == false or form was not posted.
const AS_TEXTBOX_EMPTY
Status: user tried to create a new section without content.
const AS_SUCCESS_UPDATE
Status: Article successfully updated.
const AS_IMAGE_REDIRECT_ANON
Status: anonymous user is not allowed to upload (User::isAllowed('upload') == false)
const AS_NO_CREATE_PERMISSION
Status: user tried to create this page, but is not allowed to do that ( Title->userCan('create') == f...
const AS_SUCCESS_NEW_ARTICLE
Status: Article successfully created.
const UNICODE_CHECK
Used for Unicode support checks.
const AS_RATE_LIMITED
Status: rate limiter for action 'edit' was tripped.
const AS_HOOK_ERROR
Status: Article update aborted by a hook function.
const AS_SUMMARY_NEEDED
Status: no edit summary given and the user has forceeditsummary set and the user is not editing in hi...
Exception representing a failure to serialize or unserialize a content object.
static plaintextParam( $plaintext)
Group all the pieces relevant to the context of a request into one instance.
setRequest(WebRequest $request)
Content object implementation for representing flat text.
Represents a title within MediaWiki.