MediaWiki  master
ApiEditPage.php
Go to the documentation of this file.
1 <?php
31 
48 class ApiEditPage extends ApiBase {
50 
53 
55  private $revisionLookup;
56 
59 
62 
65 
76  public function __construct(
77  ApiMain $mainModule,
78  $moduleName,
85  ) {
86  parent::__construct( $mainModule, $moduleName );
87 
88  // This class is extended and therefor fallback to global state - T264213
89  $services = MediaWikiServices::getInstance();
90  $this->contentHandlerFactory = $contentHandlerFactory ?? $services->getContentHandlerFactory();
91  $this->revisionLookup = $revisionLookup ?? $services->getRevisionLookup();
92  $this->watchedItemStore = $watchedItemStore ?? $services->getWatchedItemStore();
93  $this->wikiPageFactory = $wikiPageFactory ?? $services->getWikiPageFactory();
94 
95  // Variables needed in ApiWatchlistTrait trait
96  $this->watchlistExpiryEnabled = $this->getConfig()->get( 'WatchlistExpiry' );
97  $this->watchlistMaxDuration = $this->getConfig()->get( 'WatchlistExpiryMaxDuration' );
98  $this->watchlistManager = $watchlistManager ?? $services->getWatchlistManager();
99  $this->userOptionsLookup = $userOptionsLookup ?? $services->getUserOptionsLookup();
100  }
101 
102  public function execute() {
103  $this->useTransactionalTimeLimit();
104 
105  $user = $this->getUser();
106  $params = $this->extractRequestParams();
107 
108  $this->requireAtLeastOneParameter( $params, 'text', 'appendtext', 'prependtext', 'undo' );
109 
110  $pageObj = $this->getTitleOrPageId( $params );
111  $titleObj = $pageObj->getTitle();
112  $this->getErrorFormatter()->setContextTitle( $titleObj );
113  $apiResult = $this->getResult();
114 
115  if ( $params['redirect'] ) {
116  if ( $params['prependtext'] === null
117  && $params['appendtext'] === null
118  && $params['section'] !== 'new'
119  ) {
120  $this->dieWithError( 'apierror-redirect-appendonly' );
121  }
122  if ( $titleObj->isRedirect() ) {
123  $oldTitle = $titleObj;
124 
125  $titles = $this->revisionLookup
126  ->getRevisionByTitle( $oldTitle, 0, IDBAccessObject::READ_LATEST )
127  ->getContent( SlotRecord::MAIN, RevisionRecord::FOR_THIS_USER, $user )
128  ->getRedirectChain();
129  // array_shift( $titles );
130  '@phan-var Title[] $titles';
131 
132  $redirValues = [];
133 
135  foreach ( $titles as $id => $newTitle ) {
136  $titles[$id - 1] = $titles[$id - 1] ?? $oldTitle;
137 
138  $redirValues[] = [
139  'from' => $titles[$id - 1]->getPrefixedText(),
140  'to' => $newTitle->getPrefixedText()
141  ];
142 
143  $titleObj = $newTitle;
144 
145  // T239428: Check whether the new title is valid
146  if ( $titleObj->isExternal() || !$titleObj->canExist() ) {
147  $redirValues[count( $redirValues ) - 1]['to'] = $titleObj->getFullText();
148  $this->dieWithError(
149  [
150  'apierror-edit-invalidredirect',
151  Message::plaintextParam( $oldTitle->getPrefixedText() ),
152  Message::plaintextParam( $titleObj->getFullText() ),
153  ],
154  'edit-invalidredirect',
155  [ 'redirects' => $redirValues ]
156  );
157  }
158  }
159 
160  ApiResult::setIndexedTagName( $redirValues, 'r' );
161  $apiResult->addValue( null, 'redirects', $redirValues );
162 
163  // Since the page changed, update $pageObj
164  $pageObj = $this->wikiPageFactory->newFromTitle( $titleObj );
165  $this->getErrorFormatter()->setContextTitle( $titleObj );
166  }
167  }
168 
169  if ( $params['contentmodel'] ) {
170  $contentHandler = $this->contentHandlerFactory->getContentHandler( $params['contentmodel'] );
171  } else {
172  $contentHandler = $pageObj->getContentHandler();
173  }
174  $contentModel = $contentHandler->getModelID();
175 
176  $name = $titleObj->getPrefixedDBkey();
177 
178  if ( $params['undo'] > 0 ) {
179  // allow undo via api
180  } elseif ( $contentHandler->supportsDirectApiEditing() === false ) {
181  $this->dieWithError( [ 'apierror-no-direct-editing', $contentModel, $name ] );
182  }
183 
184  $contentFormat = $params['contentformat'] ?: $contentHandler->getDefaultFormat();
185 
186  if ( !$contentHandler->isSupportedFormat( $contentFormat ) ) {
187  $this->dieWithError( [ 'apierror-badformat', $contentFormat, $contentModel, $name ] );
188  }
189 
190  if ( $params['createonly'] && $titleObj->exists() ) {
191  $this->dieWithError( 'apierror-articleexists' );
192  }
193  if ( $params['nocreate'] && !$titleObj->exists() ) {
194  $this->dieWithError( 'apierror-missingtitle' );
195  }
196 
197  // Now let's check whether we're even allowed to do this
199  $titleObj,
200  'edit',
201  [ 'autoblock' => true ]
202  );
203 
204  $toMD5 = $params['text'];
205  if ( $params['appendtext'] !== null || $params['prependtext'] !== null ) {
206  $content = $pageObj->getContent();
207 
208  if ( !$content ) {
209  if ( $titleObj->getNamespace() === NS_MEDIAWIKI ) {
210  # If this is a MediaWiki:x message, then load the messages
211  # and return the message value for x.
212  $text = $titleObj->getDefaultMessageText();
213  if ( $text === false ) {
214  $text = '';
215  }
216 
217  try {
218  $content = ContentHandler::makeContent( $text, $titleObj );
219  } catch ( MWContentSerializationException $ex ) {
220  $this->dieWithException( $ex, [
221  'wrap' => ApiMessage::create( 'apierror-contentserializationexception', 'parseerror' )
222  ] );
223  }
224  } else {
225  # Otherwise, make a new empty content.
226  $content = $contentHandler->makeEmptyContent();
227  }
228  }
229 
230  // @todo Add support for appending/prepending to the Content interface
231 
232  if ( !( $content instanceof TextContent ) ) {
233  $this->dieWithError( [ 'apierror-appendnotsupported', $contentModel ] );
234  }
235 
236  if ( $params['section'] !== null ) {
237  if ( !$contentHandler->supportsSections() ) {
238  $this->dieWithError( [ 'apierror-sectionsnotsupported', $contentModel ] );
239  }
240 
241  if ( $params['section'] == 'new' ) {
242  // DWIM if they're trying to prepend/append to a new section.
243  $content = null;
244  } else {
245  // Process the content for section edits
246  $section = $params['section'];
247  $content = $content->getSection( $section );
248 
249  if ( !$content ) {
250  $this->dieWithError( [ 'apierror-nosuchsection', wfEscapeWikiText( $section ) ] );
251  }
252  }
253  }
254 
255  if ( !$content ) {
256  $text = '';
257  } else {
258  $text = $content->serialize( $contentFormat );
259  }
260 
261  $params['text'] = $params['prependtext'] . $text . $params['appendtext'];
262  $toMD5 = $params['prependtext'] . $params['appendtext'];
263  }
264 
265  if ( $params['undo'] > 0 ) {
266  $undoRev = $this->revisionLookup->getRevisionById( $params['undo'] );
267  if ( $undoRev === null || $undoRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
268  $this->dieWithError( [ 'apierror-nosuchrevid', $params['undo'] ] );
269  }
270 
271  if ( $params['undoafter'] > 0 ) {
272  $undoafterRev = $this->revisionLookup->getRevisionById( $params['undoafter'] );
273  }
274  if ( $params['undoafter'] == 0 ) {
275  $undoafterRev = $this->revisionLookup->getPreviousRevision( $undoRev );
276  }
277  if ( $undoafterRev === null || $undoafterRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
278  $this->dieWithError( [ 'apierror-nosuchrevid', $params['undoafter'] ] );
279  }
280 
281  if ( $undoRev->getPageId() != $pageObj->getId() ) {
282  $this->dieWithError( [ 'apierror-revwrongpage', $undoRev->getId(),
283  $titleObj->getPrefixedText() ] );
284  }
285  if ( $undoafterRev->getPageId() != $pageObj->getId() ) {
286  $this->dieWithError( [ 'apierror-revwrongpage', $undoafterRev->getId(),
287  $titleObj->getPrefixedText() ] );
288  }
289 
290  $newContent = $contentHandler->getUndoContent(
291  $pageObj->getRevisionRecord()->getContent( SlotRecord::MAIN ),
292  $undoRev->getContent( SlotRecord::MAIN ),
293  $undoafterRev->getContent( SlotRecord::MAIN ),
294  $pageObj->getRevisionRecord()->getId() === $undoRev->getId()
295  );
296 
297  if ( !$newContent ) {
298  $this->dieWithError( 'undo-failure', 'undofailure' );
299  }
300  if ( !$params['contentmodel'] && !$params['contentformat'] ) {
301  // If we are reverting content model, the new content model
302  // might not support the current serialization format, in
303  // which case go back to the old serialization format,
304  // but only if the user hasn't specified a format/model
305  // parameter.
306  if ( !$newContent->isSupportedFormat( $contentFormat ) ) {
307  $undoafterRevMainSlot = $undoafterRev->getSlot(
308  SlotRecord::MAIN,
309  RevisionRecord::RAW
310  );
311  $contentFormat = $undoafterRevMainSlot->getFormat();
312  if ( !$contentFormat ) {
313  // fall back to default content format for the model
314  // of $undoafterRev
315  $contentFormat = $this->contentHandlerFactory
316  ->getContentHandler( $undoafterRevMainSlot->getModel() )
317  ->getDefaultFormat();
318  }
319  }
320  // Override content model with model of undid revision.
321  $contentModel = $newContent->getModel();
322  $undoContentModel = true;
323  }
324  $params['text'] = $newContent->serialize( $contentFormat );
325  // If no summary was given and we only undid one rev,
326  // use an autosummary
327 
328  if ( $params['summary'] === null ) {
329  $nextRev = $this->revisionLookup->getNextRevision( $undoafterRev );
330  if ( $nextRev && $nextRev->getId() == $params['undo'] ) {
331  $undoRevUser = $undoRev->getUser();
332  $params['summary'] = wfMessage( 'undo-summary' )
333  ->params( $params['undo'], $undoRevUser ? $undoRevUser->getName() : '' )
334  ->inContentLanguage()->text();
335  }
336  }
337  }
338 
339  // See if the MD5 hash checks out
340  if ( $params['md5'] !== null && md5( $toMD5 ) !== $params['md5'] ) {
341  $this->dieWithError( 'apierror-badmd5' );
342  }
343 
344  // EditPage wants to parse its stuff from a WebRequest
345  // That interface kind of sucks, but it's workable
346  $requestArray = [
347  'wpTextbox1' => $params['text'],
348  'format' => $contentFormat,
349  'model' => $contentModel,
350  'wpEditToken' => $params['token'],
351  'wpIgnoreBlankSummary' => true,
352  'wpIgnoreBlankArticle' => true,
353  'wpIgnoreSelfRedirect' => true,
354  'bot' => $params['bot'],
355  'wpUnicodeCheck' => EditPage::UNICODE_CHECK,
356  ];
357 
358  if ( $params['summary'] !== null ) {
359  $requestArray['wpSummary'] = $params['summary'];
360  }
361 
362  if ( $params['sectiontitle'] !== null ) {
363  $requestArray['wpSectionTitle'] = $params['sectiontitle'];
364  }
365 
366  if ( $params['undo'] > 0 ) {
367  $requestArray['wpUndidRevision'] = $params['undo'];
368  }
369  if ( $params['undoafter'] > 0 ) {
370  $requestArray['wpUndoAfter'] = $params['undoafter'];
371  }
372 
373  // Skip for baserevid == null or '' or '0' or 0
374  if ( !empty( $params['baserevid'] ) ) {
375  $requestArray['editRevId'] = $params['baserevid'];
376  }
377 
378  // Watch out for basetimestamp == '' or '0'
379  // It gets treated as NOW, almost certainly causing an edit conflict
380  if ( $params['basetimestamp'] !== null && (bool)$this->getMain()->getVal( 'basetimestamp' ) ) {
381  $requestArray['wpEdittime'] = $params['basetimestamp'];
382  } elseif ( empty( $params['baserevid'] ) ) {
383  // Only set if baserevid is not set. Otherwise, conflicts would be ignored,
384  // due to the way userWasLastToEdit() works.
385  $requestArray['wpEdittime'] = $pageObj->getTimestamp();
386  }
387 
388  if ( $params['starttimestamp'] !== null ) {
389  $requestArray['wpStarttime'] = $params['starttimestamp'];
390  } else {
391  $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime
392  }
393 
394  if ( $params['minor'] || ( !$params['notminor'] &&
395  $this->userOptionsLookup->getOption( $user, 'minordefault' ) )
396  ) {
397  $requestArray['wpMinoredit'] = '';
398  }
399 
400  if ( $params['recreate'] ) {
401  $requestArray['wpRecreate'] = '';
402  }
403 
404  if ( $params['section'] !== null ) {
405  $section = $params['section'];
406  if ( !preg_match( '/^((T-)?\d+|new)$/', $section ) ) {
407  $this->dieWithError( 'apierror-invalidsection' );
408  }
409  $content = $pageObj->getContent();
410  if ( $section !== '0'
411  && $section != 'new'
412  && ( !$content || !$content->getSection( $section ) )
413  ) {
414  $this->dieWithError( [ 'apierror-nosuchsection', $section ] );
415  }
416  $requestArray['wpSection'] = $params['section'];
417  } else {
418  $requestArray['wpSection'] = '';
419  }
420 
421  $watch = $this->getWatchlistValue( $params['watchlist'], $titleObj, $user );
422 
423  // Deprecated parameters
424  if ( $params['watch'] ) {
425  $watch = true;
426  } elseif ( $params['unwatch'] ) {
427  $watch = false;
428  }
429 
430  if ( $watch ) {
431  $requestArray['wpWatchthis'] = true;
432  $watchlistExpiry = $this->getExpiryFromParams( $params );
433 
434  if ( $watchlistExpiry ) {
435  $requestArray['wpWatchlistExpiry'] = $watchlistExpiry;
436  }
437  }
438 
439  // Apply change tags
440  if ( $params['tags'] ) {
441  $tagStatus = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $this->getAuthority() );
442  if ( $tagStatus->isOK() ) {
443  $requestArray['wpChangeTags'] = implode( ',', $params['tags'] );
444  } else {
445  $this->dieStatus( $tagStatus );
446  }
447  }
448 
449  // Pass through anything else we might have been given, to support extensions
450  // This is kind of a hack but it's the best we can do to make extensions work
451  $requestArray += $this->getRequest()->getValues();
452 
453  global $wgTitle, $wgRequest;
454 
455  $req = new DerivativeRequest( $this->getRequest(), $requestArray, true );
456 
457  // Some functions depend on $wgTitle == $ep->mTitle
458  // TODO: Make them not or check if they still do
459  $wgTitle = $titleObj;
460 
461  $articleContext = new RequestContext;
462  $articleContext->setRequest( $req );
463  $articleContext->setWikiPage( $pageObj );
464  $articleContext->setUser( $this->getUser() );
465 
467  $articleObject = Article::newFromWikiPage( $pageObj, $articleContext );
468 
469  $ep = new EditPage( $articleObject );
470 
471  $ep->setApiEditOverride( true );
472  $ep->setContextTitle( $titleObj );
473  $ep->importFormData( $req );
474 
475  // T255700: Ensure content models of the base content
476  // and fetched revision remain the same before attempting to save.
477  $editRevId = $requestArray['editRevId'] ?? false;
478  $baseRev = $this->revisionLookup->getRevisionByTitle( $titleObj, $editRevId );
479  $baseContentModel = null;
480 
481  if ( $baseRev ) {
482  $baseContent = $baseRev->getContent( SlotRecord::MAIN );
483  $baseContentModel = $baseContent ? $baseContent->getModel() : null;
484  }
485 
486  if ( $baseContentModel === null ) {
487  $baseContentModel = $pageObj->getContentModel();
488  }
489 
490  // However, allow the content models to possibly differ if we are intentionally
491  // changing them or we are doing an undo edit that is reverting content model change.
492  $contentModelsCanDiffer = $params['contentmodel'] || isset( $undoContentModel );
493 
494  if ( !$contentModelsCanDiffer && $contentModel !== $baseContentModel ) {
495  $this->dieWithError( [ 'apierror-contentmodel-mismatch', $contentModel, $baseContentModel ] );
496  }
497 
498  // Do the actual save
499  $oldRevId = $articleObject->getRevIdFetched();
500  $result = null;
501 
502  // Fake $wgRequest for some hooks inside EditPage
503  // @todo FIXME: This interface SUCKS
504  $oldRequest = $wgRequest;
505  $wgRequest = $req;
506 
507  $status = $ep->attemptSave( $result );
508  $statusValue = is_int( $status->value ) ? $status->value : 0;
509  $wgRequest = $oldRequest;
510 
511  $r = [];
512  switch ( $statusValue ) {
515  if ( isset( $status->apiHookResult ) ) {
516  $r = $status->apiHookResult;
517  $r['result'] = 'Failure';
518  $apiResult->addValue( null, $this->getModuleName(), $r );
519  return;
520  }
521  if ( !$status->getErrors() ) {
522  // This appears to be unreachable right now, because all
523  // code paths will set an error. Could change, though.
524  $status->fatal( 'hookaborted' ); // @codeCoverageIgnore
525  }
526  $this->dieStatus( $status );
527 
528  // These two cases will normally have been caught earlier, and will
529  // only occur if something blocks the user between the earlier
530  // check and the check in EditPage (presumably a hook). It's not
531  // obvious that this is even possible.
532  // @codeCoverageIgnoreStart
534  $this->dieBlocked( $user->getBlock() );
535  // dieBlocked prevents continuation
536 
538  $this->dieReadOnly();
539  // @codeCoverageIgnoreEnd
540 
542  $r['new'] = true;
543  // fall-through
544 
546  $r['result'] = 'Success';
547  $r['pageid'] = (int)$titleObj->getArticleID();
548  $r['title'] = $titleObj->getPrefixedText();
549  $r['contentmodel'] = $articleObject->getPage()->getContentModel();
550  $newRevId = $articleObject->getPage()->getLatest();
551  if ( $newRevId == $oldRevId ) {
552  $r['nochange'] = true;
553  } else {
554  $r['oldrevid'] = (int)$oldRevId;
555  $r['newrevid'] = (int)$newRevId;
556  $r['newtimestamp'] = wfTimestamp( TS_ISO_8601,
557  $pageObj->getTimestamp() );
558  }
559 
560  if ( $watch ) {
561  $r['watched'] = true;
562 
563  $watchlistExpiry = $this->getWatchlistExpiry(
564  $this->watchedItemStore,
565  $titleObj,
566  $user
567  );
568 
569  if ( $watchlistExpiry ) {
570  $r['watchlistexpiry'] = $watchlistExpiry;
571  }
572  }
573  break;
574 
575  default:
576  if ( !$status->getErrors() ) {
577  // EditPage sometimes only sets the status code without setting
578  // any actual error messages. Supply defaults for those cases.
579  switch ( $statusValue ) {
580  // Currently needed
582  $status->fatal( 'apierror-noimageredirect-anon' );
583  break;
585  $status->fatal( 'apierror-noimageredirect' );
586  break;
589  $status->fatal( 'apierror-contenttoobig', $this->getConfig()->get( 'MaxArticleSize' ) );
590  break;
592  $status->fatal( 'apierror-noedit-anon' );
593  break;
595  $status->fatal( 'apierror-cantchangecontentmodel' );
596  break;
598  $status->fatal( 'apierror-pagedeleted' );
599  break;
601  $status->fatal( 'edit-conflict' );
602  break;
603 
604  // Currently shouldn't be needed, but here in case
605  // hooks use them without setting appropriate
606  // errors on the status.
607  // @codeCoverageIgnoreStart
609  $status->fatal( 'apierror-spamdetected', $result['spam'] );
610  break;
612  $status->fatal( 'apierror-noedit' );
613  break;
615  $status->fatal( 'apierror-ratelimited' );
616  break;
618  $status->fatal( 'nocreate-loggedin' );
619  break;
621  $status->fatal( 'apierror-emptypage' );
622  break;
624  $status->fatal( 'apierror-emptynewsection' );
625  break;
627  $status->fatal( 'apierror-summaryrequired' );
628  break;
629  default:
630  wfWarn( __METHOD__ . ": Unknown EditPage code $statusValue with no message" );
631  $status->fatal( 'apierror-unknownerror-editpage', $statusValue );
632  break;
633  // @codeCoverageIgnoreEnd
634  }
635  }
636  $this->dieStatus( $status );
637  }
638  $apiResult->addValue( null, $this->getModuleName(), $r );
639  }
640 
641  public function mustBePosted() {
642  return true;
643  }
644 
645  public function isWriteMode() {
646  return true;
647  }
648 
649  public function getAllowedParams() {
650  $params = [
651  'title' => [
652  ApiBase::PARAM_TYPE => 'string',
653  ],
654  'pageid' => [
655  ApiBase::PARAM_TYPE => 'integer',
656  ],
657  'section' => null,
658  'sectiontitle' => [
659  ApiBase::PARAM_TYPE => 'string',
660  ],
661  'text' => [
662  ApiBase::PARAM_TYPE => 'text',
663  ],
664  'summary' => null,
665  'tags' => [
666  ApiBase::PARAM_TYPE => 'tags',
667  ApiBase::PARAM_ISMULTI => true,
668  ],
669  'minor' => false,
670  'notminor' => false,
671  'bot' => false,
672  'baserevid' => [
673  ApiBase::PARAM_TYPE => 'integer',
674  ],
675  'basetimestamp' => [
676  ApiBase::PARAM_TYPE => 'timestamp',
677  ],
678  'starttimestamp' => [
679  ApiBase::PARAM_TYPE => 'timestamp',
680  ],
681  'recreate' => false,
682  'createonly' => false,
683  'nocreate' => false,
684  'watch' => [
685  ApiBase::PARAM_DFLT => false,
687  ],
688  'unwatch' => [
689  ApiBase::PARAM_DFLT => false,
691  ],
692  ];
693 
694  // Params appear in the docs in the order they are defined,
695  // which is why this is here and not at the bottom.
696  $params += $this->getWatchlistParams();
697 
698  return $params + [
699  'md5' => null,
700  'prependtext' => [
701  ApiBase::PARAM_TYPE => 'text',
702  ],
703  'appendtext' => [
704  ApiBase::PARAM_TYPE => 'text',
705  ],
706  'undo' => [
707  ApiBase::PARAM_TYPE => 'integer',
708  ApiBase::PARAM_MIN => 0,
710  ],
711  'undoafter' => [
712  ApiBase::PARAM_TYPE => 'integer',
713  ApiBase::PARAM_MIN => 0,
715  ],
716  'redirect' => [
717  ApiBase::PARAM_TYPE => 'boolean',
718  ApiBase::PARAM_DFLT => false,
719  ],
720  'contentformat' => [
721  ApiBase::PARAM_TYPE => $this->contentHandlerFactory->getAllContentFormats(),
722  ],
723  'contentmodel' => [
724  ApiBase::PARAM_TYPE => $this->contentHandlerFactory->getContentModels(),
725  ],
726  'token' => [
727  // Standard definition automatically inserted
728  ApiBase::PARAM_HELP_MSG_APPEND => [ 'apihelp-edit-param-token' ],
729  ],
730  ];
731  }
732 
733  public function needsToken() {
734  return 'csrf';
735  }
736 
737  protected function getExamplesMessages() {
738  return [
739  'action=edit&title=Test&summary=test%20summary&' .
740  'text=article%20content&baserevid=1234567&token=123ABC'
741  => 'apihelp-edit-example-edit',
742  'action=edit&title=Test&summary=NOTOC&minor=&' .
743  'prependtext=__NOTOC__%0A&basetimestamp=2007-08-24T12:34:54Z&token=123ABC'
744  => 'apihelp-edit-example-prepend',
745  'action=edit&title=Test&undo=13585&undoafter=13579&' .
746  'basetimestamp=2007-08-24T12:34:54Z&token=123ABC'
747  => 'apihelp-edit-example-undo',
748  ];
749  }
750 
751  public function getHelpUrls() {
752  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Edit';
753  }
754 }
ApiBase\checkTitleUserPermissions
checkTitleUserPermissions( $pageIdentity, $actions, array $options=[])
Helper function for permission-denied errors.
Definition: ApiBase.php:1571
ApiEditPage
A module that allows for editing and creating pages.
Definition: ApiEditPage.php:48
MediaWiki\EditPage\IEditObject\AS_READ_ONLY_PAGE_ANON
const AS_READ_ONLY_PAGE_ANON
Status: this anonymous user is not allowed to edit this page.
Definition: IEditObject.php:50
DerivativeRequest
Similar to FauxRequest, but only fakes URL parameters and method (POST or GET) and use the base reque...
Definition: DerivativeRequest.php:36
ApiMain
This is the main API class, used for both external and internal processing.
Definition: ApiMain.php:49
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:72
MediaWiki\EditPage\IEditObject\AS_ARTICLE_WAS_DELETED
const AS_ARTICLE_WAS_DELETED
Status: article was deleted while editing and wpRecreate == false or form was not posted.
Definition: IEditObject.php:62
MediaWiki\EditPage\IEditObject\AS_SUCCESS_NEW_ARTICLE
const AS_SUCCESS_NEW_ARTICLE
Status: Article successfully created.
Definition: IEditObject.php:35
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
MediaWiki\EditPage\IEditObject\AS_BLOCKED_PAGE_FOR_USER
const AS_BLOCKED_PAGE_FOR_USER
Status: User is blocked from editing this page.
Definition: IEditObject.php:44
ApiEditPage\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiEditPage.php:737
MediaWiki\EditPage\IEditObject\AS_TEXTBOX_EMPTY
const AS_TEXTBOX_EMPTY
Status: user tried to create a new section without content.
Definition: IEditObject.php:80
getExpiryFromParams
getExpiryFromParams(array $params)
Get formatted expiry from the given parameters, or null if no expiry was provided.
Definition: ApiWatchlistTrait.php:164
RequestContext\setRequest
setRequest(WebRequest $request)
Definition: RequestContext.php:128
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
MediaWiki\EditPage\IEditObject\AS_SUMMARY_NEEDED
const AS_SUMMARY_NEEDED
Status: no edit summary given and the user has forceeditsummary set and the user is not editing in hi...
Definition: IEditObject.php:77
MediaWiki\EditPage\IEditObject\AS_CONFLICT_DETECTED
const AS_CONFLICT_DETECTED
Status: (non-resolvable) edit conflict.
Definition: IEditObject.php:71
ApiEditPage\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiEditPage.php:751
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:200
$wgRequest
$wgRequest
Definition: Setup.php:722
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:1436
ApiEditPage\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiEditPage.php:649
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1665
ApiBase\getTitleOrPageId
getTitleOrPageId( $params, $load=false)
Get a WikiPage object from a title or pageid param, if possible.
Definition: ApiBase.php:1033
ApiBase\PARAM_TYPE
const PARAM_TYPE
Definition: ApiBase.php:81
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:628
MediaWiki\EditPage\IEditObject\AS_SPAM_ERROR
const AS_SPAM_ERROR
Status: summary contained spam according to one of the regexes in $wgSummarySpamRegex.
Definition: IEditObject.php:89
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1183
MediaWiki\EditPage\IEditObject\AS_CONTENT_TOO_BIG
const AS_CONTENT_TOO_BIG
Status: Content too big (> $wgMaxArticleSize)
Definition: IEditObject.php:47
ContextSource\getRequest
getRequest()
Definition: ContextSource.php:81
MediaWiki\EditPage\IEditObject\AS_NO_CREATE_PERMISSION
const AS_NO_CREATE_PERMISSION
Status: user tried to create this page, but is not allowed to do that.
Definition: IEditObject.php:65
MediaWiki\EditPage\IEditObject\AS_SUCCESS_UPDATE
const AS_SUCCESS_UPDATE
Status: Article successfully updated.
Definition: IEditObject.php:32
ApiEditPage\mustBePosted
mustBePosted()
Indicates whether this module must be called with a POST request.
Definition: ApiEditPage.php:641
ContextSource\getUser
getUser()
Definition: ContextSource.php:136
ApiBase\PARAM_HELP_MSG_APPEND
const PARAM_HELP_MSG_APPEND
((string|array|Message)[]) Specify additional i18n messages to append to the normal message for this ...
Definition: ApiBase.php:169
MediaWiki\Revision\RevisionLookup
Service for looking up page revisions.
Definition: RevisionLookup.php:38
ApiBase
This abstract class implements many basic API functions, and is the base of all API classes.
Definition: ApiBase.php:55
ApiBase\dieBlocked
dieBlocked(Block $block)
Throw an ApiUsageException, which will (if uncaught) call the main module's error handler and die wit...
Definition: ApiBase.php:1466
ApiEditPage\$contentHandlerFactory
IContentHandlerFactory $contentHandlerFactory
Definition: ApiEditPage.php:52
ApiBase\PARAM_DEPRECATED
const PARAM_DEPRECATED
Definition: ApiBase.php:101
ApiEditPage\$userOptionsLookup
UserOptionsLookup $userOptionsLookup
Definition: ApiEditPage.php:64
ApiBase\PARAM_MIN
const PARAM_MIN
Definition: ApiBase.php:93
Article\newFromWikiPage
static newFromWikiPage(WikiPage $page, IContextSource $context)
Create an Article object of the appropriate class for the given page.
Definition: Article.php:195
MediaWiki\EditPage\IEditObject\AS_RATE_LIMITED
const AS_RATE_LIMITED
Status: rate limiter for action 'edit' was tripped.
Definition: IEditObject.php:59
EditPage\UNICODE_CHECK
const UNICODE_CHECK
Used for Unicode support checks.
Definition: EditPage.php:96
MediaWiki\Watchlist\WatchlistManager
WatchlistManager service.
Definition: WatchlistManager.php:52
ApiEditPage\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiEditPage.php:102
ApiEditPage\$wikiPageFactory
WikiPageFactory $wikiPageFactory
Definition: ApiEditPage.php:61
MWContentSerializationException
Exception representing a failure to serialize or unserialize a content object.
Definition: MWContentSerializationException.php:8
Page\WikiPageFactory
Definition: WikiPageFactory.php:19
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:764
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1694
RequestContext
Group all the pieces relevant to the context of a request into one instance @newable.
Definition: RequestContext.php:41
ApiBase\requireAtLeastOneParameter
requireAtLeastOneParameter( $params,... $required)
Die if none of a certain set of parameters is set and not false.
Definition: ApiBase.php:961
ApiMessage\create
static create( $msg, $code=null, array $data=null)
Create an IApiMessage for the message.
Definition: ApiMessage.php:43
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:148
$wgTitle
$wgTitle
Definition: Setup.php:869
ChangeTags\canAddTagsAccompanyingChange
static canAddTagsAccompanyingChange(array $tags, Authority $performer=null)
Is it OK to allow the user to apply all the specified tags at the same time as they edit/make the cha...
Definition: ChangeTags.php:625
MediaWiki\EditPage\IEditObject\AS_NO_CHANGE_CONTENT_MODEL
const AS_NO_CHANGE_CONTENT_MODEL
Status: user tried to modify the content model, but is not allowed to do that ( User::isAllowed('edit...
Definition: IEditObject.php:101
MediaWiki\EditPage\IEditObject\AS_READ_ONLY_PAGE_LOGGED
const AS_READ_ONLY_PAGE_LOGGED
Status: this logged in user is not allowed to edit this page.
Definition: IEditObject.php:53
ApiEditPage\$watchedItemStore
WatchedItemStoreInterface $watchedItemStore
Definition: ApiEditPage.php:58
getWatchlistValue
getWatchlistValue(string $watchlist, Title $title, User $user, ?string $userOption=null)
Return true if we're to watch the page, false if not.
Definition: ApiWatchlistTrait.php:116
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:603
$content
$content
Definition: router.php:76
MediaWiki\Content\IContentHandlerFactory
Definition: IContentHandlerFactory.php:10
ContextSource\getAuthority
getAuthority()
Definition: ContextSource.php:144
MediaWiki\EditPage\IEditObject\AS_IMAGE_REDIRECT_ANON
const AS_IMAGE_REDIRECT_ANON
Status: anonymous user is not allowed to upload (User::isAllowed('upload') == false)
Definition: IEditObject.php:92
Message\plaintextParam
static plaintextParam( $plaintext)
Definition: Message.php:1248
ApiBase\dieReadOnly
dieReadOnly()
Helper function for readonly errors.
Definition: ApiBase.php:1528
ApiBase\useTransactionalTimeLimit
useTransactionalTimeLimit()
Call wfTransactionalTimeLimit() if this request was POSTed.
Definition: ApiBase.php:1293
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1456
ApiEditPage\__construct
__construct(ApiMain $mainModule, $moduleName, IContentHandlerFactory $contentHandlerFactory=null, RevisionLookup $revisionLookup=null, WatchedItemStoreInterface $watchedItemStore=null, WikiPageFactory $wikiPageFactory=null, WatchlistManager $watchlistManager=null, UserOptionsLookup $userOptionsLookup=null)
Definition: ApiEditPage.php:76
ApiEditPage\isWriteMode
isWriteMode()
Indicates whether this module requires write mode.
Definition: ApiEditPage.php:645
MediaWiki\User\UserOptionsLookup
Provides access to user options.
Definition: UserOptionsLookup.php:29
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:39
EditPage
The edit page/HTML interface (split from Article) The actual database and text munging is still in Ar...
Definition: EditPage.php:89
ApiWatchlistTrait
trait ApiWatchlistTrait
An ApiWatchlistTrait adds class properties and convenience methods for APIs that allow you to watch a...
Definition: ApiWatchlistTrait.php:21
$watchlistManager
WatchlistManager $watchlistManager
Definition: ApiWatchlistTrait.php:30
ApiBase\PARAM_RANGE_ENFORCE
const PARAM_RANGE_ENFORCE
(boolean) Inverse of IntegerDef::PARAM_IGNORE_RANGE
Definition: ApiBase.php:152
getWatchlistExpiry
getWatchlistExpiry(WatchedItemStoreInterface $store, Title $title, UserIdentity $user)
Get existing expiry from the database.
Definition: ApiWatchlistTrait.php:181
MediaWiki\EditPage\IEditObject\AS_BLANK_ARTICLE
const AS_BLANK_ARTICLE
Status: user tried to create a blank page and wpIgnoreBlankArticle == false.
Definition: IEditObject.php:68
Title
Represents a title within MediaWiki.
Definition: Title.php:47
getWatchlistParams
getWatchlistParams(array $watchOptions=[])
Get additional allow params specific to watchlisting.
Definition: ApiWatchlistTrait.php:59
MediaWiki\EditPage\IEditObject\AS_IMAGE_REDIRECT_LOGGED
const AS_IMAGE_REDIRECT_LOGGED
Status: logged in user is not allowed to upload (User::isAllowed('upload') == false)
Definition: IEditObject.php:95
MediaWiki\EditPage\IEditObject\AS_MAX_ARTICLE_SIZE_EXCEEDED
const AS_MAX_ARTICLE_SIZE_EXCEEDED
Status: article is too big (> $wgMaxArticleSize), after merging in the new section.
Definition: IEditObject.php:83
ApiBase\PARAM_DFLT
const PARAM_DFLT
Definition: ApiBase.php:73
MediaWiki\EditPage\IEditObject\AS_READ_ONLY_PAGE
const AS_READ_ONLY_PAGE
Status: wiki is in readonly mode (wfReadOnly() == true)
Definition: IEditObject.php:56
ApiBase\dieWithException
dieWithException(Throwable $exception, array $options=[])
Abort execution with an error derived from a throwable.
Definition: ApiBase.php:1449
ApiBase\dieStatus
dieStatus(StatusValue $status)
Throw an ApiUsageException based on the Status object.
Definition: ApiBase.php:1499
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:497
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
Definition: ApiBase.php:77
ApiBase\getMain
getMain()
Get the main module.
Definition: ApiBase.php:513
ApiEditPage\$revisionLookup
RevisionLookup $revisionLookup
Definition: ApiEditPage.php:55
wfWarn
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
Definition: GlobalFunctions.php:1043
MediaWiki\EditPage\IEditObject\AS_HOOK_ERROR_EXPECTED
const AS_HOOK_ERROR_EXPECTED
Status: A hook function returned an error.
Definition: IEditObject.php:41
ApiEditPage\needsToken
needsToken()
Returns the token type this module requires in order to execute.
Definition: ApiEditPage.php:733
WatchedItemStoreInterface
Definition: WatchedItemStoreInterface.php:31
ApiBase\getErrorFormatter
getErrorFormatter()
Definition: ApiBase.php:639
MediaWiki\EditPage\IEditObject\AS_HOOK_ERROR
const AS_HOOK_ERROR
Status: Article update aborted by a hook function.
Definition: IEditObject.php:38
MediaWiki\Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40