MediaWiki  master
PageUpdater.php
Go to the documentation of this file.
1 <?php
25 namespace MediaWiki\Storage;
26 
28 use ChangeTags;
30 use Content;
31 use ContentHandler;
32 use DeferredUpdates;
33 use InvalidArgumentException;
34 use LogicException;
35 use ManualLogEntry;
46 use MWException;
47 use RecentChange;
48 use Revision;
49 use RuntimeException;
50 use Status;
51 use Title;
52 use User;
53 use Wikimedia\Assert\Assert;
58 use WikiPage;
59 
76 class PageUpdater {
77 
81  private $user;
82 
86  private $wikiPage;
87 
92 
96  private $loadBalancer;
97 
101  private $revisionStore;
102 
107 
112 
116  private $hookRunner;
117 
121  private $hookContainer;
122 
128 
133 
137  private $usePageCreationLog = true;
138 
142  private $ajaxEditStash = true;
143 
147  private $originalRevId = false;
148 
152  private $tags = [];
153 
157  private $undidRevId = 0;
158 
162  private $slotsUpdate;
163 
167  private $status = null;
168 
179  public function __construct(
180  User $user,
188  ) {
189  $this->user = $user;
190  $this->wikiPage = $wikiPage;
191  $this->derivedDataUpdater = $derivedDataUpdater;
192 
193  $this->loadBalancer = $loadBalancer;
194  $this->revisionStore = $revisionStore;
195  $this->slotRoleRegistry = $slotRoleRegistry;
196  $this->contentHandlerFactory = $contentHandlerFactory;
197  $this->hookContainer = $hookContainer;
198  $this->hookRunner = new HookRunner( $hookContainer );
199 
200  $this->slotsUpdate = new RevisionSlotsUpdate();
201  }
202 
211  $this->useAutomaticEditSummaries = $useAutomaticEditSummaries;
212  }
213 
223  public function setRcPatrolStatus( $status ) {
224  $this->rcPatrolStatus = $status;
225  }
226 
234  public function setUsePageCreationLog( $use ) {
235  $this->usePageCreationLog = $use;
236  }
237 
242  public function setAjaxEditStash( $ajaxEditStash ) {
243  $this->ajaxEditStash = $ajaxEditStash;
244  }
245 
246  private function getWikiId() {
247  return false; // TODO: get from RevisionStore!
248  }
249 
255  private function getDBConnectionRef( $mode ) {
256  return $this->loadBalancer->getConnectionRef( $mode, [], $this->getWikiId() );
257  }
258 
262  private function getLinkTarget() {
263  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
264  return $this->wikiPage->getTitle();
265  }
266 
270  private function getTitle() {
271  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
272  return $this->wikiPage->getTitle();
273  }
274 
278  private function getWikiPage() {
279  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
280  return $this->wikiPage;
281  }
282 
316  public function hasEditConflict( $expectedParentRevision ) {
317  $parent = $this->grabParentRevision();
318  $parentId = $parent ? $parent->getId() : 0;
319 
320  return $parentId !== $expectedParentRevision;
321  }
322 
350  public function grabParentRevision() {
351  return $this->derivedDataUpdater->grabCurrentRevision();
352  }
353 
360  private function checkFlags( $flags ) {
361  if ( !( $flags & EDIT_NEW ) && !( $flags & EDIT_UPDATE ) ) {
362  $flags |= ( $this->derivedDataUpdater->pageExisted() ) ? EDIT_UPDATE : EDIT_NEW;
363  }
364 
365  return $flags;
366  }
367 
374  public function setContent( $role, Content $content ) {
375  $this->ensureRoleAllowed( $role );
376 
377  $this->slotsUpdate->modifyContent( $role, $content );
378  }
379 
385  public function setSlot( SlotRecord $slot ) {
386  $this->ensureRoleAllowed( $slot->getRole() );
387 
388  $this->slotsUpdate->modifySlot( $slot );
389  }
390 
405  public function inheritSlot( SlotRecord $originalSlot ) {
406  // NOTE: slots can be inherited even if the role is not "allowed" on the title.
407  // NOTE: this slot is inherited from some other revision, but it's
408  // a "modified" slot for the RevisionSlotsUpdate and DerivedPageDataUpdater,
409  // since it's not implicitly inherited from the parent revision.
410  $inheritedSlot = SlotRecord::newInherited( $originalSlot );
411  $this->slotsUpdate->modifySlot( $inheritedSlot );
412  }
413 
423  public function removeSlot( $role ) {
424  $this->ensureRoleNotRequired( $role );
425 
426  $this->slotsUpdate->removeSlot( $role );
427  }
428 
435  public function getOriginalRevisionId() {
436  return $this->originalRevId;
437  }
438 
451  Assert::parameterType( 'integer|boolean', $originalRevId, '$originalRevId' );
452  $this->originalRevId = $originalRevId;
453  }
454 
461  public function getUndidRevisionId() {
462  return $this->undidRevId;
463  }
464 
472  public function setUndidRevisionId( $undidRevId ) {
473  Assert::parameterType( 'integer', $undidRevId, '$undidRevId' );
474  $this->undidRevId = $undidRevId;
475  }
476 
483  public function addTag( $tag ) {
484  Assert::parameterType( 'string', $tag, '$tag' );
485  $this->tags[] = trim( $tag );
486  }
487 
494  public function addTags( array $tags ) {
495  Assert::parameterElementType( 'string', $tags, '$tags' );
496  foreach ( $tags as $tag ) {
497  $this->addTag( $tag );
498  }
499  }
500 
506  public function getExplicitTags() {
507  return $this->tags;
508  }
509 
514  private function computeEffectiveTags( $flags ) {
515  $tags = $this->tags;
516 
517  foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
518  $old_content = $this->getParentContent( $role );
519 
520  $handler = $this->getContentHandler( $role );
521  $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
522 
523  // TODO: MCR: Do this for all slots. Also add tags for removing roles!
524  $tag = $handler->getChangeTag( $old_content, $content, $flags );
525  // If there is no applicable tag, null is returned, so we need to check
526  if ( $tag ) {
527  $tags[] = $tag;
528  }
529  }
530 
531  // Check for undo tag
532  if ( $this->undidRevId !== 0 && in_array( 'mw-undo', ChangeTags::getSoftwareTags() ) ) {
533  $tags[] = 'mw-undo';
534  }
535 
536  return array_unique( $tags );
537  }
538 
546  private function getParentContent( $role ) {
547  $parent = $this->grabParentRevision();
548 
549  if ( $parent && $parent->hasSlot( $role ) ) {
550  return $parent->getContent( $role, RevisionRecord::RAW );
551  }
552 
553  return null;
554  }
555 
560  private function getContentHandler( $role ) {
561  if ( $this->slotsUpdate->isModifiedSlot( $role ) ) {
562  $slot = $this->slotsUpdate->getModifiedSlot( $role );
563  } else {
564  $parent = $this->grabParentRevision();
565 
566  if ( $parent ) {
567  $slot = $parent->getSlot( $role, RevisionRecord::RAW );
568  } else {
569  throw new RevisionAccessException( 'No such slot: ' . $role );
570  }
571  }
572 
573  return $this->contentHandlerFactory->getContentHandler( $slot->getModel() );
574  }
575 
581  private function makeAutoSummary( $flags ) {
582  if ( !$this->useAutomaticEditSummaries || ( $flags & EDIT_AUTOSUMMARY ) === 0 ) {
584  }
585 
586  // NOTE: this generates an auto-summary for SOME RANDOM changed slot!
587  // TODO: combine auto-summaries for multiple slots!
588  // XXX: this logic should not be in the storage layer!
589  $roles = $this->slotsUpdate->getModifiedRoles();
590  $role = reset( $roles );
591 
592  if ( $role === false ) {
594  }
595 
596  $handler = $this->getContentHandler( $role );
597  $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
598  $old_content = $this->getParentContent( $role );
599  $summary = $handler->getAutosummary( $old_content, $content, $flags );
600 
601  return CommentStoreComment::newUnsavedComment( $summary );
602  }
603 
649  public function saveRevision( CommentStoreComment $summary, $flags = 0 ) {
650  // Defend against mistakes caused by differences with the
651  // signature of WikiPage::doEditContent.
652  Assert::parameterType( 'integer', $flags, '$flags' );
653 
654  if ( $this->wasCommitted() ) {
655  throw new RuntimeException( 'saveRevision() has already been called on this PageUpdater!' );
656  }
657 
658  // Low-level sanity check
659  if ( $this->getLinkTarget()->getText() === '' ) {
660  throw new RuntimeException( 'Something is trying to edit an article with an empty title' );
661  }
662 
663  // NOTE: slots can be inherited even if the role is not "allowed" on the title.
665  $this->checkAllRolesAllowed(
666  $this->slotsUpdate->getModifiedRoles(),
667  $status
668  );
669  $this->checkNoRolesRequired(
670  $this->slotsUpdate->getRemovedRoles(),
671  $status
672  );
673 
674  if ( !$status->isOK() ) {
675  return null;
676  }
677 
678  // Make sure the given content is allowed in the respective slots of this page
679  foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
680  $slot = $this->slotsUpdate->getModifiedSlot( $role );
681  $roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
682 
683  if ( !$roleHandler->isAllowedModel( $slot->getModel(), $this->getTitle() ) ) {
684  $contentHandler = $this->contentHandlerFactory
685  ->getContentHandler( $slot->getModel() );
686  $this->status = Status::newFatal( 'content-not-allowed-here',
687  ContentHandler::getLocalizedName( $contentHandler->getModelID() ),
688  $this->getTitle()->getPrefixedText(),
689  wfMessage( $roleHandler->getNameMessageKey() )
690  // TODO: defer message lookup to caller
691  );
692  return null;
693  }
694  }
695 
696  // Load the data from the master database if needed. Needed to check flags.
697  // NOTE: This grabs the parent revision as the CAS token, if grabParentRevision
698  // wasn't called yet. If the page is modified by another process before we are done with
699  // it, this method must fail (with status 'edit-conflict')!
700  // NOTE: The parent revision may be different from $this->originalRevisionId.
701  $this->grabParentRevision();
702  $flags = $this->checkFlags( $flags );
703 
704  // Avoid statsd noise and wasted cycles check the edit stash (T136678)
705  if ( ( $flags & EDIT_INTERNAL ) || ( $flags & EDIT_FORCE_BOT ) ) {
706  $useStashed = false;
707  } else {
708  $useStashed = $this->ajaxEditStash;
709  }
710 
711  $user = $this->user;
712 
713  // Prepare the update. This performs PST and generates the canonical ParserOutput.
714  $this->derivedDataUpdater->prepareContent(
715  $this->user,
716  $this->slotsUpdate,
717  $useStashed
718  );
719 
720  // TODO: don't force initialization here!
721  // This is a hack to work around the fact that late initialization of the ParserOutput
722  // causes ApiFlowEditHeaderTest::testCache to fail. Whether that failure indicates an
723  // actual problem, or is just an issue with the test setup, remains to be determined
724  // [dk, 2018-03].
725  // Anomie said in 2018-03:
726  /*
727  I suspect that what's breaking is this:
728 
729  The old version of WikiPage::doEditContent() called prepareContentForEdit() which
730  generated the ParserOutput right then, so when doEditUpdates() gets called from the
731  DeferredUpdate scheduled by WikiPage::doCreate() there's no need to parse. I note
732  there's a comment there that says "Get the pre-save transform content and final
733  parser output".
734  The new version of WikiPage::doEditContent() makes a PageUpdater and calls its
735  saveRevision(), which calls DerivedPageDataUpdater::prepareContent() and
736  PageUpdater::doCreate() without ever having to actually generate a ParserOutput.
737  Thus, when DerivedPageDataUpdater::doUpdates() is called from the DeferredUpdate
738  scheduled by PageUpdater::doCreate(), it does find that it needs to parse at that point.
739 
740  And the order of operations in that Flow test is presumably:
741 
742  - Create a page with a call to WikiPage::doEditContent(), in a way that somehow avoids
743  processing the DeferredUpdate.
744  - Set up the "no set!" mock cache in Flow\Tests\Api\ApiTestCase::expectCacheInvalidate()
745  - Then, during the course of doing that test, a $db->commit() results in the
746  DeferredUpdates being run.
747  */
748  $this->derivedDataUpdater->getCanonicalParserOutput();
749 
750  // Trigger pre-save hook (using provided edit summary)
751  $renderedRevision = $this->derivedDataUpdater->getRenderedRevision();
752  $hookStatus = Status::newGood( [] );
753  $allowedByHook = $this->hookRunner->onMultiContentSave(
754  $renderedRevision, $user, $summary, $flags, $hookStatus
755  );
756  if ( $allowedByHook && $this->hookContainer->isRegistered( 'PageContentSave' ) ) {
757  // Also run the legacy hook.
758  // NOTE: WikiPage should only be used for the legacy hook,
759  // and only if something uses the legacy hook.
760  $mainContent = $this->derivedDataUpdater->getSlots()->getContent( SlotRecord::MAIN );
761 
762  // Deprecated since 1.35.
763  $allowedByHook = $this->hookRunner->onPageContentSave(
764  $this->getWikiPage(), $user, $mainContent, $summary,
765  $flags & EDIT_MINOR, null, null, $flags, $hookStatus
766  );
767  }
768 
769  if ( !$allowedByHook ) {
770  // The hook has prevented this change from being saved.
771  if ( $hookStatus->isOK() ) {
772  // Hook returned false but didn't call fatal(); use generic message
773  $hookStatus->fatal( 'edit-hook-aborted' );
774  }
775 
776  $this->status = $hookStatus;
777  return null;
778  }
779 
780  // Provide autosummaries if one is not provided and autosummaries are enabled
781  // XXX: $summary == null seems logical, but the empty string may actually come from the user
782  // XXX: Move this logic out of the storage layer! It does not belong here! Use a callback?
783  if ( $summary->text === '' && $summary->data === null ) {
784  $summary = $this->makeAutoSummary( $flags );
785  }
786 
787  // Actually create the revision and create/update the page.
788  // Do NOT yet set $this->status!
789  if ( $flags & EDIT_UPDATE ) {
790  $status = $this->doModify( $summary, $this->user, $flags );
791  } else {
792  $status = $this->doCreate( $summary, $this->user, $flags );
793  }
794 
795  // Promote user to any groups they meet the criteria for
796  DeferredUpdates::addCallableUpdate( function () use ( $user ) {
797  $user->addAutopromoteOnceGroups( 'onEdit' );
798  $user->addAutopromoteOnceGroups( 'onView' ); // b/c
799  } );
800 
801  // NOTE: set $this->status only after all hooks have been called,
802  // so wasCommitted doesn't return true when called indirectly from a hook handler!
803  $this->status = $status;
804 
805  // TODO: replace bad status with Exceptions!
806  return ( $this->status && $this->status->isOK() )
807  ? $this->status->value['revision-record']
808  : null;
809  }
810 
816  public function wasCommitted() {
817  return $this->status !== null;
818  }
819 
843  public function getStatus() {
844  return $this->status;
845  }
846 
852  public function wasSuccessful() {
853  return $this->status && $this->status->isOK();
854  }
855 
861  public function isNew() {
862  return $this->status && $this->status->isOK() && $this->status->value['new'];
863  }
864 
872  public function isUnchanged() {
873  return $this->status
874  && $this->status->isOK()
875  && $this->status->value['revision-record'] === null;
876  }
877 
884  public function getNewRevision() {
885  return ( $this->status && $this->status->isOK() )
886  ? $this->status->value['revision-record']
887  : null;
888  }
889 
905  private function makeNewRevision(
906  CommentStoreComment $comment,
907  User $user,
908  $flags,
910  ) {
911  $wikiPage = $this->getWikiPage();
912  $title = $this->getTitle();
913  $parent = $this->grabParentRevision();
914 
915  // XXX: we expect to get a MutableRevisionRecord here, but that's a bit brittle!
916  // TODO: introduce something like an UnsavedRevisionFactory service instead!
918  $rev = $this->derivedDataUpdater->getRevision();
919  '@phan-var MutableRevisionRecord $rev';
920 
921  // Avoid fatal error when the Title's ID changed, T204793
922  if (
923  $rev->getPageId() !== null && $title->exists()
924  && $rev->getPageId() !== $title->getArticleID()
925  ) {
926  $titlePageId = $title->getArticleID();
927  $revPageId = $rev->getPageId();
928  $masterPageId = $title->getArticleID( Title::READ_LATEST );
929 
930  if ( $revPageId === $masterPageId ) {
931  wfWarn( __METHOD__ . ": Encountered stale Title object: old ID was $titlePageId, "
932  . "continuing with new ID from master, $masterPageId" );
933  } else {
934  throw new InvalidArgumentException(
935  "Revision inherited page ID $revPageId from its parent, "
936  . "but the provided Title object belongs to page ID $masterPageId"
937  );
938  }
939  }
940 
941  $rev->setPageId( $title->getArticleID() );
942 
943  if ( $parent ) {
944  $oldid = $parent->getId();
945  $rev->setParentId( $oldid );
946  } else {
947  $oldid = 0;
948  }
949 
950  $rev->setComment( $comment );
951  $rev->setUser( $user );
952  $rev->setMinorEdit( ( $flags & EDIT_MINOR ) > 0 );
953 
954  foreach ( $rev->getSlots()->getSlots() as $slot ) {
955  $content = $slot->getContent();
956 
957  // XXX: We may push this up to the "edit controller" level, see T192777.
958  // XXX: prepareSave() and isValid() could live in SlotRoleHandler
959  // XXX: PrepareSave should not take a WikiPage!
960  $prepStatus = $content->prepareSave( $wikiPage, $flags, $oldid, $user );
961 
962  // TODO: MCR: record which problem arose in which slot.
963  $status->merge( $prepStatus );
964  }
965 
966  $this->checkAllRequiredRoles(
967  $rev->getSlotRoles(),
968  $status
969  );
970 
971  return $rev;
972  }
973 
982  private function doModify( CommentStoreComment $summary, User $user, $flags ) {
983  $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
984 
985  // Update article, but only if changed.
986  $status = Status::newGood( [ 'new' => false, 'revision' => null, 'revision-record' => null ] );
987 
988  $oldRev = $this->grabParentRevision();
989  $oldid = $oldRev ? $oldRev->getId() : 0;
990 
991  if ( !$oldRev ) {
992  // Article gone missing
993  $status->fatal( 'edit-gone-missing' );
994 
995  return $status;
996  }
997 
998  $newRevisionRecord = $this->makeNewRevision(
999  $summary,
1000  $user,
1001  $flags,
1002  $status
1003  );
1004 
1005  if ( !$status->isOK() ) {
1006  return $status;
1007  }
1008 
1009  $now = $newRevisionRecord->getTimestamp();
1010 
1011  // XXX: we may want a flag that allows a null revision to be forced!
1012  $changed = $this->derivedDataUpdater->isChange();
1013 
1014  $dbw = $this->getDBConnectionRef( DB_MASTER );
1015 
1016  if ( $changed ) {
1017  $dbw->startAtomic( __METHOD__ );
1018 
1019  // Get the latest page_latest value while locking it.
1020  // Do a CAS style check to see if it's the same as when this method
1021  // started. If it changed then bail out before touching the DB.
1022  $latestNow = $wikiPage->lockAndGetLatest(); // TODO: move to storage service, pass DB
1023  if ( $latestNow != $oldid ) {
1024  // We don't need to roll back, since we did not modify the database yet.
1025  // XXX: Or do we want to rollback, any transaction started by calling
1026  // code will fail? If we want that, we should probably throw an exception.
1027  $dbw->endAtomic( __METHOD__ );
1028  // Page updated or deleted in the mean time
1029  $status->fatal( 'edit-conflict' );
1030 
1031  return $status;
1032  }
1033 
1034  // At this point we are now comitted to returning an OK
1035  // status unless some DB query error or other exception comes up.
1036  // This way callers don't have to call rollback() if $status is bad
1037  // unless they actually try to catch exceptions (which is rare).
1038 
1039  // Save revision content and meta-data
1040  $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1041 
1042  // Update page_latest and friends to reflect the new revision
1043  // TODO: move to storage service
1044  $wasRedirect = $this->derivedDataUpdater->wasRedirect();
1045  if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, null, $wasRedirect ) ) {
1046  throw new PageUpdateException( "Failed to update page row to use new revision." );
1047  }
1048 
1049  $tags = $this->computeEffectiveTags( $flags );
1050  $this->hookRunner->onRevisionFromEditComplete(
1051  $wikiPage, $newRevisionRecord, $this->getOriginalRevisionId(), $user, $tags
1052  );
1053 
1054  // TODO: replace legacy hook!
1055  $newLegacyRevision = new Revision( $newRevisionRecord );
1056  $this->hookRunner->onNewRevisionFromEditComplete(
1057  $wikiPage, $newLegacyRevision, $this->getOriginalRevisionId(), $user, $tags );
1058 
1059  // Update recentchanges
1060  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1061  // Add RC row to the DB
1063  $now,
1064  $this->getTitle(),
1065  $newRevisionRecord->isMinor(),
1066  $user,
1067  $summary->text, // TODO: pass object when that becomes possible
1068  $oldid,
1069  $newRevisionRecord->getTimestamp(),
1070  ( $flags & EDIT_FORCE_BOT ) > 0,
1071  '',
1072  $oldRev->getSize(),
1073  $newRevisionRecord->getSize(),
1074  $newRevisionRecord->getId(),
1075  $this->rcPatrolStatus,
1076  $tags
1077  );
1078  }
1079 
1080  $user->incEditCount();
1081 
1082  $dbw->endAtomic( __METHOD__ );
1083 
1084  // Return the new revision to the caller
1085  $status->value['revision-record'] = $newRevisionRecord;
1086 
1087  // TODO: globally replace usages of 'revision' with getNewRevision()
1088  $status->value['revision'] = $newLegacyRevision;
1089  } else {
1090  // T34948: revision ID must be set to page {{REVISIONID}} and
1091  // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
1092  // Since we don't insert a new revision into the database, the least
1093  // error-prone way is to reuse given old revision.
1094  $newRevisionRecord = $oldRev;
1095 
1096  $status->warning( 'edit-no-change' );
1097  // Update page_touched as updateRevisionOn() was not called.
1098  // Other cache updates are managed in WikiPage::onArticleEdit()
1099  // via WikiPage::doEditUpdates().
1100  $this->getTitle()->invalidateCache( $now );
1101  }
1102 
1103  // Do secondary updates once the main changes have been committed...
1104  // NOTE: the updates have to be processed before sending the response to the client
1105  // (DeferredUpdates::PRESEND), otherwise the client may already be following the
1106  // HTTP redirect to the standard view before derived data has been created - most
1107  // importantly, before the parser cache has been updated. This would cause the
1108  // content to be parsed a second time, or may cause stale content to be shown.
1110  $this->getAtomicSectionUpdate(
1111  $dbw,
1112  $wikiPage,
1113  $newRevisionRecord,
1114  $user,
1115  $summary,
1116  $flags,
1117  $status,
1118  [ 'changed' => $changed, ]
1119  ),
1121  );
1122 
1123  return $status;
1124  }
1125 
1135  private function doCreate( CommentStoreComment $summary, User $user, $flags ) {
1136  $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1137 
1138  if ( !$this->derivedDataUpdater->getSlots()->hasSlot( SlotRecord::MAIN ) ) {
1139  throw new PageUpdateException( 'Must provide a main slot when creating a page!' );
1140  }
1141 
1142  $status = Status::newGood( [ 'new' => true, 'revision' => null, 'revision-record' => null ] );
1143 
1144  $newRevisionRecord = $this->makeNewRevision(
1145  $summary,
1146  $user,
1147  $flags,
1148  $status
1149  );
1150 
1151  if ( !$status->isOK() ) {
1152  return $status;
1153  }
1154 
1155  $now = $newRevisionRecord->getTimestamp();
1156 
1157  $dbw = $this->getDBConnectionRef( DB_MASTER );
1158  $dbw->startAtomic( __METHOD__ );
1159 
1160  // Add the page record unless one already exists for the title
1161  // TODO: move to storage service
1162  $newid = $wikiPage->insertOn( $dbw );
1163  if ( $newid === false ) {
1164  $dbw->endAtomic( __METHOD__ );
1165  $status->fatal( 'edit-already-exists' );
1166 
1167  return $status;
1168  }
1169 
1170  // At this point we are now comitted to returning an OK
1171  // status unless some DB query error or other exception comes up.
1172  // This way callers don't have to call rollback() if $status is bad
1173  // unless they actually try to catch exceptions (which is rare).
1174  $newRevisionRecord->setPageId( $newid );
1175 
1176  // Save the revision text...
1177  $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1178 
1179  // Update the page record with revision data
1180  // TODO: move to storage service
1181  if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, 0 ) ) {
1182  throw new PageUpdateException( "Failed to update page row to use new revision." );
1183  }
1184 
1185  $tags = $this->computeEffectiveTags( $flags );
1186  $this->hookRunner->onRevisionFromEditComplete(
1187  $wikiPage, $newRevisionRecord, false, $user, $tags
1188  );
1189 
1190  // TODO: replace legacy hook!
1191  $newLegacyRevision = new Revision( $newRevisionRecord );
1192  $this->hookRunner->onNewRevisionFromEditComplete(
1193  $wikiPage, $newLegacyRevision, false, $user, $tags );
1194 
1195  // Update recentchanges
1196  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1197  // Add RC row to the DB
1199  $now,
1200  $this->getTitle(),
1201  $newRevisionRecord->isMinor(),
1202  $user,
1203  $summary->text, // TODO: pass object when that becomes possible
1204  ( $flags & EDIT_FORCE_BOT ) > 0,
1205  '',
1206  $newRevisionRecord->getSize(),
1207  $newRevisionRecord->getId(),
1208  $this->rcPatrolStatus,
1209  $tags
1210  );
1211  }
1212 
1213  $user->incEditCount();
1214 
1215  if ( $this->usePageCreationLog ) {
1216  // Log the page creation
1217  // @TODO: Do we want a 'recreate' action?
1218  $logEntry = new ManualLogEntry( 'create', 'create' );
1219  $logEntry->setPerformer( $user );
1220  $logEntry->setTarget( $this->getTitle() );
1221  $logEntry->setComment( $summary->text );
1222  $logEntry->setTimestamp( $now );
1223  $logEntry->setAssociatedRevId( $newRevisionRecord->getId() );
1224  $logEntry->insert();
1225  // Note that we don't publish page creation events to recentchanges
1226  // (i.e. $logEntry->publish()) since this would create duplicate entries,
1227  // one for the edit and one for the page creation.
1228  }
1229 
1230  $dbw->endAtomic( __METHOD__ );
1231 
1232  // Return the new revision to the caller
1233  // TODO: globally replace usages of 'revision' with getNewRevision()
1234  $status->value['revision'] = $newLegacyRevision;
1235  $status->value['revision-record'] = $newRevisionRecord;
1236 
1237  // Do secondary updates once the main changes have been committed...
1239  $this->getAtomicSectionUpdate(
1240  $dbw,
1241  $wikiPage,
1242  $newRevisionRecord,
1243  $user,
1244  $summary,
1245  $flags,
1246  $status,
1247  [ 'created' => true ]
1248  ),
1250  );
1251 
1252  return $status;
1253  }
1254 
1255  private function getAtomicSectionUpdate(
1256  IDatabase $dbw,
1258  RevisionRecord $newRevisionRecord,
1259  User $user,
1260  CommentStoreComment $summary,
1261  $flags,
1262  Status $status,
1263  $hints = []
1264  ) {
1265  return new AtomicSectionUpdate(
1266  $dbw,
1267  __METHOD__,
1268  function () use (
1269  $wikiPage, $newRevisionRecord, $user,
1270  $summary, $flags, $status, $hints
1271  ) {
1272  // set debug data
1273  $hints['causeAction'] = 'edit-page';
1274  $hints['causeAgent'] = $user->getName();
1275 
1276  $newLegacyRevision = new Revision( $newRevisionRecord );
1277  $mainContent = $newRevisionRecord->getContent( SlotRecord::MAIN, RevisionRecord::RAW );
1278 
1279  // Update links tables, site stats, etc.
1280  $this->derivedDataUpdater->prepareUpdate( $newRevisionRecord, $hints );
1281  $this->derivedDataUpdater->doUpdates();
1282 
1283  // TODO: replace legacy hook!
1284 
1285  if ( $hints['created'] ?? false ) {
1286  // Trigger post-create hook
1287  $this->hookRunner->onPageContentInsertComplete( $wikiPage, $user,
1288  $mainContent, $summary->text, $flags & EDIT_MINOR,
1289  null, null, $flags, $newLegacyRevision );
1290  }
1291 
1292  // Trigger post-save hook
1293  $this->hookRunner->onPageContentSaveComplete( $wikiPage, $user, $mainContent,
1294  $summary->text, $flags & EDIT_MINOR, null,
1295  null, $flags, $newLegacyRevision, $status,
1296  $this->getOriginalRevisionId(), $this->undidRevId );
1297  }
1298  );
1299  }
1300 
1304  private function getRequiredSlotRoles() {
1305  return $this->slotRoleRegistry->getRequiredRoles( $this->getTitle() );
1306  }
1307 
1311  private function getAllowedSlotRoles() {
1312  return $this->slotRoleRegistry->getAllowedRoles( $this->getTitle() );
1313  }
1314 
1315  private function ensureRoleAllowed( $role ) {
1316  $allowedRoles = $this->getAllowedSlotRoles();
1317  if ( !in_array( $role, $allowedRoles ) ) {
1318  throw new PageUpdateException( "Slot role `$role` is not allowed." );
1319  }
1320  }
1321 
1322  private function ensureRoleNotRequired( $role ) {
1323  $requiredRoles = $this->getRequiredSlotRoles();
1324  if ( in_array( $role, $requiredRoles ) ) {
1325  throw new PageUpdateException( "Slot role `$role` is required." );
1326  }
1327  }
1328 
1329  private function checkAllRolesAllowed( array $roles, Status $status ) {
1330  $allowedRoles = $this->getAllowedSlotRoles();
1331 
1332  $forbidden = array_diff( $roles, $allowedRoles );
1333  if ( !empty( $forbidden ) ) {
1334  $status->error(
1335  'edit-slots-cannot-add',
1336  count( $forbidden ),
1337  implode( ', ', $forbidden )
1338  );
1339  }
1340  }
1341 
1342  private function checkNoRolesRequired( array $roles, Status $status ) {
1343  $requiredRoles = $this->getRequiredSlotRoles();
1344 
1345  $needed = array_diff( $roles, $requiredRoles );
1346  if ( !empty( $needed ) ) {
1347  $status->error(
1348  'edit-slots-cannot-remove',
1349  count( $needed ),
1350  implode( ', ', $needed )
1351  );
1352  }
1353  }
1354 
1355  private function checkAllRequiredRoles( array $roles, Status $status ) {
1356  $requiredRoles = $this->getRequiredSlotRoles();
1357 
1358  $missing = array_diff( $requiredRoles, $roles );
1359  if ( !empty( $missing ) ) {
1360  $status->error(
1361  'edit-slots-missing',
1362  count( $missing ),
1363  implode( ', ', $missing )
1364  );
1365  }
1366  }
1367 
1368 }
MediaWiki\Storage\PageUpdater\addTags
addTags(array $tags)
Sets tags to apply to this update.
Definition: PageUpdater.php:494
MediaWiki\Storage\PageUpdater\makeNewRevision
makeNewRevision(CommentStoreComment $comment, User $user, $flags, Status $status)
Constructs a MutableRevisionRecord based on the Content prepared by the DerivedPageDataUpdater.
Definition: PageUpdater.php:905
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:57
CommentStoreComment\newUnsavedComment
static newUnsavedComment( $comment, array $data=null)
Create a new, unsaved CommentStoreComment.
Definition: CommentStoreComment.php:66
Revision\RevisionAccessException
Exception representing a failure to look up a revision.
Definition: RevisionAccessException.php:33
MediaWiki\Storage\PageUpdater\saveRevision
saveRevision(CommentStoreComment $summary, $flags=0)
Change an existing article or create a new article.
Definition: PageUpdater.php:649
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
MediaWiki\Storage\PageUpdater\$tags
array $tags
Definition: PageUpdater.php:152
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:69
MediaWiki\Storage\PageUpdater\setUndidRevisionId
setUndidRevisionId( $undidRevId)
Sets the ID of revision that was undone by the present update.
Definition: PageUpdater.php:472
MediaWiki\Storage\PageUpdater\setAjaxEditStash
setAjaxEditStash( $ajaxEditStash)
Definition: PageUpdater.php:242
MediaWiki\Storage\PageUpdater\doModify
doModify(CommentStoreComment $summary, User $user, $flags)
Definition: PageUpdater.php:982
MediaWiki\Storage\PageUpdater\$usePageCreationLog
bool $usePageCreationLog
whether to create a log entry for new page creations.
Definition: PageUpdater.php:137
WikiPage\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Update the page record to point to a newly saved revision.
Definition: WikiPage.php:1386
Revision\SlotRecord\newInherited
static newInherited(SlotRecord $slot)
Constructs a new SlotRecord for a new revision, inheriting the content of the given SlotRecord of a p...
Definition: SlotRecord.php:103
EDIT_FORCE_BOT
const EDIT_FORCE_BOT
Definition: Defines.php:145
MediaWiki\Storage\PageUpdater\checkNoRolesRequired
checkNoRolesRequired(array $roles, Status $status)
Definition: PageUpdater.php:1342
StatusValue\error
error( $message,... $parameters)
Add an error, do not set fatal flag This can be used for non-fatal errors.
Definition: StatusValue.php:193
EDIT_INTERNAL
const EDIT_INTERNAL
Definition: Defines.php:148
MediaWiki\Storage\PageUpdater\inheritSlot
inheritSlot(SlotRecord $originalSlot)
Explicitly inherit a slot from some earlier revision.
Definition: PageUpdater.php:405
User\incEditCount
incEditCount()
Schedule a deferred update to update the user's edit count.
Definition: User.php:4660
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:79
MediaWiki\Storage\PageUpdater\setOriginalRevisionId
setOriginalRevisionId( $originalRevId)
Sets the ID of an earlier revision that is being repeated or restored by this update.
Definition: PageUpdater.php:450
MediaWiki\Storage\PageUpdater\$ajaxEditStash
boolean $ajaxEditStash
see $wgAjaxEditStash
Definition: PageUpdater.php:142
MediaWiki\Storage\PageUpdater\getWikiId
getWikiId()
Definition: PageUpdater.php:246
RecentChange
Utility class for creating new RC entries.
Definition: RecentChange.php:71
MediaWiki\Storage\PageUpdater\$useAutomaticEditSummaries
boolean $useAutomaticEditSummaries
see $wgUseAutomaticEditSummaries
Definition: PageUpdater.php:127
MediaWiki\Storage\PageUpdater\getExplicitTags
getExplicitTags()
Returns the list of tags set using the addTag() method.
Definition: PageUpdater.php:506
StatusValue\warning
warning( $message,... $parameters)
Add a new warning.
Definition: StatusValue.php:178
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred update queue for execution at the appropriate time.
Definition: DeferredUpdates.php:106
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:49
StatusValue\fatal
fatal( $message,... $parameters)
Add an error and set OK to false, indicating that the operation as a whole was fatal.
Definition: StatusValue.php:208
MediaWiki\Storage\PageUpdater\setUsePageCreationLog
setUsePageCreationLog( $use)
Whether to create a log entry for new page creations.
Definition: PageUpdater.php:234
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1198
MediaWiki\Storage\PageUpdater\getParentContent
getParentContent( $role)
Returns the content of the given slot of the parent revision, with no audience checks applied.
Definition: PageUpdater.php:546
MediaWiki\Storage\PageUpdater\$wikiPage
WikiPage $wikiPage
Definition: PageUpdater.php:86
MediaWiki\Storage\PageUpdater\$loadBalancer
ILoadBalancer $loadBalancer
Definition: PageUpdater.php:96
MediaWiki\Storage\PageUpdater\wasCommitted
wasCommitted()
Whether saveRevision() has been called on this instance.
Definition: PageUpdater.php:816
MediaWiki\Storage\PageUpdater\getOriginalRevisionId
getOriginalRevisionId()
Returns the ID of an earlier revision that is being repeated or restored by this update.
Definition: PageUpdater.php:435
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
MediaWiki\Storage\PageUpdater\ensureRoleAllowed
ensureRoleAllowed( $role)
Definition: PageUpdater.php:1315
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:42
MediaWiki\Storage\PageUpdater\doCreate
doCreate(CommentStoreComment $summary, User $user, $flags)
Definition: PageUpdater.php:1135
Revision
Definition: Revision.php:39
MediaWiki\Storage\PageUpdater\makeAutoSummary
makeAutoSummary( $flags)
Definition: PageUpdater.php:581
MediaWiki\Storage\PageUpdater\$revisionStore
RevisionStore $revisionStore
Definition: PageUpdater.php:101
MediaWiki\Storage\PageUpdater\checkFlags
checkFlags( $flags)
Check flags and add EDIT_NEW or EDIT_UPDATE to them as needed.
Definition: PageUpdater.php:360
MediaWiki\Storage\PageUpdater\wasSuccessful
wasSuccessful()
Whether saveRevision() completed successfully.
Definition: PageUpdater.php:852
MWException
MediaWiki exception.
Definition: MWException.php:26
MediaWiki\Storage\PageUpdater\setContent
setContent( $role, Content $content)
Set the new content for the given slot role.
Definition: PageUpdater.php:374
MediaWiki\Storage\PageUpdater\getTitle
getTitle()
Definition: PageUpdater.php:270
ChangeTags
Definition: ChangeTags.php:30
Revision\SlotRecord\getRole
getRole()
Returns the role of the slot.
Definition: SlotRecord.php:489
StatusValue\isOK
isOK()
Returns whether the operation completed.
Definition: StatusValue.php:130
WikiPage\insertOn
insertOn( $dbw, $pageId=null)
Insert a new empty page record for this article.
Definition: WikiPage.php:1341
DeferredUpdates
Class for managing the deferred updates.
Definition: DeferredUpdates.php:62
MediaWiki\Storage\PageUpdater\getNewRevision
getNewRevision()
The new revision created by saveRevision(), or null if saveRevision() has not yet been called,...
Definition: PageUpdater.php:884
MediaWiki\Storage\PageUpdater\computeEffectiveTags
computeEffectiveTags( $flags)
Definition: PageUpdater.php:514
MediaWiki\Storage\PageUpdater\getAtomicSectionUpdate
getAtomicSectionUpdate(IDatabase $dbw, WikiPage $wikiPage, RevisionRecord $newRevisionRecord, User $user, CommentStoreComment $summary, $flags, Status $status, $hints=[])
Definition: PageUpdater.php:1255
MediaWiki\Storage\PageUpdater\getWikiPage
getWikiPage()
Definition: PageUpdater.php:278
RecentChange\notifyEdit
static notifyEdit( $timestamp, $title, $minor, $user, $comment, $oldId, $lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0, $tags=[])
Makes an entry in the database corresponding to an edit.
Definition: RecentChange.php:637
StatusValue\merge
merge( $other, $overwriteValue=false)
Merge another status object into this one.
Definition: StatusValue.php:223
Revision\RevisionRecord\RAW
const RAW
Definition: RevisionRecord.php:60
ChangeTags\getSoftwareTags
static getSoftwareTags( $all=false)
Loads defined core tags, checks for invalid types (if not array), and filters for supported and enabl...
Definition: ChangeTags.php:64
$title
$title
Definition: testCompression.php:38
MediaWiki\Storage\PageUpdater\removeSlot
removeSlot( $role)
Removes the slot with the given role.
Definition: PageUpdater.php:423
DB_MASTER
const DB_MASTER
Definition: defines.php:26
MediaWiki\Storage\PageUpdater\getRequiredSlotRoles
getRequiredSlotRoles()
Definition: PageUpdater.php:1304
WikiPage\lockAndGetLatest
lockAndGetLatest()
Lock the page row for this title+id and return page_latest (or 0)
Definition: WikiPage.php:3056
MediaWiki\Storage\PageUpdater\$hookContainer
HookContainer $hookContainer
Definition: PageUpdater.php:121
MediaWiki\Storage\PageUpdater\checkAllRolesAllowed
checkAllRolesAllowed(array $roles, Status $status)
Definition: PageUpdater.php:1329
MediaWiki\Storage\PageUpdater\addTag
addTag( $tag)
Sets a tag to apply to this update.
Definition: PageUpdater.php:483
MediaWiki\Storage\PageUpdater\getAllowedSlotRoles
getAllowedSlotRoles()
Definition: PageUpdater.php:1311
AtomicSectionUpdate
Deferrable Update for closure/callback updates via IDatabase::doAtomicSection()
Definition: AtomicSectionUpdate.php:9
MediaWiki\Storage\RevisionSlotsUpdate
Value object representing a modification of revision slots.
Definition: RevisionSlotsUpdate.php:36
$content
$content
Definition: router.php:76
MediaWiki\Storage\PageUpdater\$user
User $user
Definition: PageUpdater.php:81
EDIT_UPDATE
const EDIT_UPDATE
Definition: Defines.php:142
ContentHandler\getLocalizedName
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
Definition: ContentHandler.php:297
MediaWiki\Content\IContentHandlerFactory
Definition: IContentHandlerFactory.php:10
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:81
Revision\MutableRevisionRecord
Definition: MutableRevisionRecord.php:43
MediaWiki\Storage\PageUpdater\$contentHandlerFactory
IContentHandlerFactory $contentHandlerFactory
Definition: PageUpdater.php:111
MediaWiki\Storage\PageUpdater
Controller-like object for creating and updating pages by creating new revisions.
Definition: PageUpdater.php:76
MediaWiki\Storage\PageUpdater\getDBConnectionRef
getDBConnectionRef( $mode)
Definition: PageUpdater.php:255
MediaWiki\Storage\PageUpdater\hasEditConflict
hasEditConflict( $expectedParentRevision)
Checks whether this update conflicts with another update performed between the client loading data to...
Definition: PageUpdater.php:316
MediaWiki\Storage\PageUpdater\$hookRunner
HookRunner $hookRunner
Definition: PageUpdater.php:116
MediaWiki\Storage
Definition: BlobAccessException.php:23
Revision\SlotRecord\MAIN
const MAIN
Definition: SlotRecord.php:41
Wikimedia\Rdbms\DBUnexpectedError
Definition: DBUnexpectedError.php:27
MediaWiki\Storage\PageUpdater\__construct
__construct(User $user, WikiPage $wikiPage, DerivedPageDataUpdater $derivedDataUpdater, ILoadBalancer $loadBalancer, RevisionStore $revisionStore, SlotRoleRegistry $slotRoleRegistry, IContentHandlerFactory $contentHandlerFactory, HookContainer $hookContainer)
Definition: PageUpdater.php:179
Content
Base interface for content objects.
Definition: Content.php:34
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:141
MediaWiki\Storage\PageUpdater\$status
Status null $status
Definition: PageUpdater.php:167
Wikimedia\Rdbms\DBConnRef
Helper class used for automatically marking an IDatabase connection as reusable (once it no longer ma...
Definition: DBConnRef.php:29
MediaWiki\Storage\PageUpdater\$slotsUpdate
RevisionSlotsUpdate $slotsUpdate
Definition: PageUpdater.php:162
Title
Represents a title within MediaWiki.
Definition: Title.php:42
EDIT_AUTOSUMMARY
const EDIT_AUTOSUMMARY
Definition: Defines.php:147
MediaWiki\Storage\PageUpdater\grabParentRevision
grabParentRevision()
Returns the revision that was the page's current revision when grabParentRevision() was first called.
Definition: PageUpdater.php:350
RecentChange\notifyNew
static notifyNew( $timestamp, $title, $minor, $user, $comment, $bot, $ip='', $size=0, $newId=0, $patrol=0, $tags=[])
Makes an entry in the database corresponding to page creation Note: the title object must be loaded w...
Definition: RecentChange.php:711
DeferredUpdates\PRESEND
const PRESEND
Definition: DeferredUpdates.php:84
MediaWiki\Storage\PageUpdater\getStatus
getStatus()
The Status object indicating whether saveRevision() was successful, or null if saveRevision() was not...
Definition: PageUpdater.php:843
MediaWiki\Storage\PageUpdater\setRcPatrolStatus
setRcPatrolStatus( $status)
Sets the "patrolled" status of the edit.
Definition: PageUpdater.php:223
MediaWiki\Storage\PageUpdater\$rcPatrolStatus
int $rcPatrolStatus
the RC patrol status the new revision should be marked with.
Definition: PageUpdater.php:132
RecentChange\PRC_UNPATROLLED
const PRC_UNPATROLLED
Definition: RecentChange.php:80
MediaWiki\Storage\PageUpdater\ensureRoleNotRequired
ensureRoleNotRequired( $role)
Definition: PageUpdater.php:1322
MediaWiki\Storage\PageUpdater\getContentHandler
getContentHandler( $role)
Definition: PageUpdater.php:560
MediaWiki\Storage\PageUpdater\getUndidRevisionId
getUndidRevisionId()
Returns the revision ID set by setUndidRevisionId(), indicating what revision is being undone by this...
Definition: PageUpdater.php:461
MediaWiki\Storage\PageUpdater\$undidRevId
int $undidRevId
Definition: PageUpdater.php:157
Revision\RevisionRecord\getContent
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of this revision.
Definition: RevisionRecord.php:167
User\addAutopromoteOnceGroups
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
Definition: User.php:1455
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:38
MediaWiki\Storage\PageUpdater\$derivedDataUpdater
DerivedPageDataUpdater $derivedDataUpdater
Definition: PageUpdater.php:91
MediaWiki\Storage\PageUpdater\checkAllRequiredRoles
checkAllRequiredRoles(array $roles, Status $status)
Definition: PageUpdater.php:1355
Revision\SlotRoleRegistry
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
Definition: SlotRoleRegistry.php:48
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:44
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:1051
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:23
EDIT_MINOR
const EDIT_MINOR
Definition: Defines.php:143
EDIT_SUPPRESS_RC
const EDIT_SUPPRESS_RC
Definition: Defines.php:144
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
MediaWiki\Storage\PageUpdateException
Exception representing a failure to update a page entry.
Definition: PageUpdateException.php:32
MediaWiki\Storage\DerivedPageDataUpdater
A handle for managing updates for derived page data on edit, import, purge, etc.
Definition: DerivedPageDataUpdater.php:103
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:55
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add a callable update.
Definition: DeferredUpdates.php:145
MediaWiki\Storage\PageUpdater\$slotRoleRegistry
SlotRoleRegistry $slotRoleRegistry
Definition: PageUpdater.php:106
MediaWiki\Storage\PageUpdater\$originalRevId
bool int $originalRevId
Definition: PageUpdater.php:147
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:2137
CommentStoreComment
CommentStoreComment represents a comment stored by CommentStore.
Definition: CommentStoreComment.php:29
MediaWiki\Storage\PageUpdater\setUseAutomaticEditSummaries
setUseAutomaticEditSummaries( $useAutomaticEditSummaries)
Can be used to enable or disable automatic summaries that are applied to certain kinds of changes,...
Definition: PageUpdater.php:210
MediaWiki\Storage\PageUpdater\isNew
isNew()
Whether saveRevision() was called and created a new page.
Definition: PageUpdater.php:861
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
MediaWiki\Storage\PageUpdater\isUnchanged
isUnchanged()
Whether saveRevision() did not create a revision because the content didn't change (null-edit).
Definition: PageUpdater.php:872
MediaWiki\Storage\PageUpdater\setSlot
setSlot(SlotRecord $slot)
Set the new slot for the given slot role.
Definition: PageUpdater.php:385
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
MediaWiki\Storage\PageUpdater\getLinkTarget
getLinkTarget()
Definition: PageUpdater.php:262