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;
48 use MWException;
49 use RecentChange;
50 use Revision;
51 use RuntimeException;
52 use Status;
53 use Title;
54 use User;
55 use Wikimedia\Assert\Assert;
60 use WikiPage;
61 
78 class PageUpdater {
79 
85  public const CONSTRUCTOR_OPTIONS = [
86  'ManualRevertSearchRadius'
87  ];
88 
92  private $user;
93 
97  private $wikiPage;
98 
103 
107  private $loadBalancer;
108 
112  private $revisionStore;
113 
118 
123 
127  private $hookRunner;
128 
132  private $hookContainer;
133 
139 
144 
148  private $usePageCreationLog = true;
149 
153  private $ajaxEditStash = true;
154 
158  private $tags = [];
159 
163  private $slotsUpdate;
164 
168  private $status = null;
169 
174 
178  private $editResult = null;
179 
191  public function __construct(
192  User $user,
200  ServiceOptions $serviceOptions
201  ) {
202  $serviceOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
203 
204  $this->user = $user;
205  $this->wikiPage = $wikiPage;
206  $this->derivedDataUpdater = $derivedDataUpdater;
207 
208  $this->loadBalancer = $loadBalancer;
209  $this->revisionStore = $revisionStore;
210  $this->slotRoleRegistry = $slotRoleRegistry;
211  $this->contentHandlerFactory = $contentHandlerFactory;
212  $this->hookContainer = $hookContainer;
213  $this->hookRunner = new HookRunner( $hookContainer );
214 
215  $this->slotsUpdate = new RevisionSlotsUpdate();
216  $this->editResultBuilder = new EditResultBuilder(
217  $this->revisionStore,
220  $serviceOptions
221  );
222  }
223 
232  $this->useAutomaticEditSummaries = $useAutomaticEditSummaries;
233  }
234 
244  public function setRcPatrolStatus( $status ) {
245  $this->rcPatrolStatus = $status;
246  }
247 
255  public function setUsePageCreationLog( $use ) {
256  $this->usePageCreationLog = $use;
257  }
258 
263  public function setAjaxEditStash( $ajaxEditStash ) {
264  $this->ajaxEditStash = $ajaxEditStash;
265  }
266 
267  private function getWikiId() {
268  return false; // TODO: get from RevisionStore!
269  }
270 
276  private function getDBConnectionRef( $mode ) {
277  return $this->loadBalancer->getConnectionRef( $mode, [], $this->getWikiId() );
278  }
279 
283  private function getLinkTarget() {
284  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
285  return $this->wikiPage->getTitle();
286  }
287 
291  private function getTitle() {
292  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
293  return $this->wikiPage->getTitle();
294  }
295 
299  private function getWikiPage() {
300  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
301  return $this->wikiPage;
302  }
303 
337  public function hasEditConflict( $expectedParentRevision ) {
338  $parent = $this->grabParentRevision();
339  $parentId = $parent ? $parent->getId() : 0;
340 
341  return $parentId !== $expectedParentRevision;
342  }
343 
371  public function grabParentRevision() {
372  return $this->derivedDataUpdater->grabCurrentRevision();
373  }
374 
381  private function checkFlags( $flags ) {
382  if ( !( $flags & EDIT_NEW ) && !( $flags & EDIT_UPDATE ) ) {
383  $flags |= ( $this->derivedDataUpdater->pageExisted() ) ? EDIT_UPDATE : EDIT_NEW;
384  }
385 
386  return $flags;
387  }
388 
395  public function setContent( $role, Content $content ) {
396  $this->ensureRoleAllowed( $role );
397 
398  $this->slotsUpdate->modifyContent( $role, $content );
399  }
400 
406  public function setSlot( SlotRecord $slot ) {
407  $this->ensureRoleAllowed( $slot->getRole() );
408 
409  $this->slotsUpdate->modifySlot( $slot );
410  }
411 
426  public function inheritSlot( SlotRecord $originalSlot ) {
427  // NOTE: slots can be inherited even if the role is not "allowed" on the title.
428  // NOTE: this slot is inherited from some other revision, but it's
429  // a "modified" slot for the RevisionSlotsUpdate and DerivedPageDataUpdater,
430  // since it's not implicitly inherited from the parent revision.
431  $inheritedSlot = SlotRecord::newInherited( $originalSlot );
432  $this->slotsUpdate->modifySlot( $inheritedSlot );
433  }
434 
444  public function removeSlot( $role ) {
445  $this->ensureRoleNotRequired( $role );
446 
447  $this->slotsUpdate->removeSlot( $role );
448  }
449 
461  public function setOriginalRevisionId( $originalRevId ) {
462  $this->editResultBuilder->setOriginalRevisionId( $originalRevId );
463  }
464 
478  public function markAsRevert(
479  int $revertMethod,
480  int $oldestRevertedRevId,
481  int $newestRevertedRevId = 0
482  ) {
483  $this->editResultBuilder->markAsRevert(
484  $revertMethod, $oldestRevertedRevId, $newestRevertedRevId
485  );
486  }
487 
495  public function getEditResult() : ?EditResult {
496  return $this->editResult;
497  }
498 
505  public function addTag( $tag ) {
506  Assert::parameterType( 'string', $tag, '$tag' );
507  $this->tags[] = trim( $tag );
508  }
509 
516  public function addTags( array $tags ) {
517  Assert::parameterElementType( 'string', $tags, '$tags' );
518  foreach ( $tags as $tag ) {
519  $this->addTag( $tag );
520  }
521  }
522 
528  public function getExplicitTags() {
529  return $this->tags;
530  }
531 
536  private function computeEffectiveTags( $flags ) {
537  $tags = $this->tags;
538  $editResult = $this->getEditResult();
539 
540  foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
541  $old_content = $this->getParentContent( $role );
542 
543  $handler = $this->getContentHandler( $role );
544  $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
545 
546  // TODO: MCR: Do this for all slots. Also add tags for removing roles!
547  $tag = $handler->getChangeTag( $old_content, $content, $flags );
548  // If there is no applicable tag, null is returned, so we need to check
549  if ( $tag ) {
550  $tags[] = $tag;
551  }
552  }
553 
554  $tags = array_merge( $tags, $editResult->getRevertTags() );
555 
556  return array_unique( $tags );
557  }
558 
566  private function getParentContent( $role ) {
567  $parent = $this->grabParentRevision();
568 
569  if ( $parent && $parent->hasSlot( $role ) ) {
570  return $parent->getContent( $role, RevisionRecord::RAW );
571  }
572 
573  return null;
574  }
575 
580  private function getContentHandler( $role ) {
581  if ( $this->slotsUpdate->isModifiedSlot( $role ) ) {
582  $slot = $this->slotsUpdate->getModifiedSlot( $role );
583  } else {
584  $parent = $this->grabParentRevision();
585 
586  if ( $parent ) {
587  $slot = $parent->getSlot( $role, RevisionRecord::RAW );
588  } else {
589  throw new RevisionAccessException( 'No such slot: ' . $role );
590  }
591  }
592 
593  return $this->contentHandlerFactory->getContentHandler( $slot->getModel() );
594  }
595 
601  private function makeAutoSummary( $flags ) {
602  if ( !$this->useAutomaticEditSummaries || ( $flags & EDIT_AUTOSUMMARY ) === 0 ) {
604  }
605 
606  // NOTE: this generates an auto-summary for SOME RANDOM changed slot!
607  // TODO: combine auto-summaries for multiple slots!
608  // XXX: this logic should not be in the storage layer!
609  $roles = $this->slotsUpdate->getModifiedRoles();
610  $role = reset( $roles );
611 
612  if ( $role === false ) {
614  }
615 
616  $handler = $this->getContentHandler( $role );
617  $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
618  $old_content = $this->getParentContent( $role );
619  $summary = $handler->getAutosummary( $old_content, $content, $flags );
620 
621  return CommentStoreComment::newUnsavedComment( $summary );
622  }
623 
669  public function saveRevision( CommentStoreComment $summary, $flags = 0 ) {
670  // Defend against mistakes caused by differences with the
671  // signature of WikiPage::doEditContent.
672  Assert::parameterType( 'integer', $flags, '$flags' );
673 
674  if ( $this->wasCommitted() ) {
675  throw new RuntimeException( 'saveRevision() has already been called on this PageUpdater!' );
676  }
677 
678  // Low-level sanity check
679  if ( $this->getLinkTarget()->getText() === '' ) {
680  throw new RuntimeException( 'Something is trying to edit an article with an empty title' );
681  }
682 
683  // NOTE: slots can be inherited even if the role is not "allowed" on the title.
685  $this->checkAllRolesAllowed(
686  $this->slotsUpdate->getModifiedRoles(),
687  $status
688  );
689  $this->checkNoRolesRequired(
690  $this->slotsUpdate->getRemovedRoles(),
691  $status
692  );
693 
694  if ( !$status->isOK() ) {
695  return null;
696  }
697 
698  // Make sure the given content is allowed in the respective slots of this page
699  foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
700  $slot = $this->slotsUpdate->getModifiedSlot( $role );
701  $roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
702 
703  if ( !$roleHandler->isAllowedModel( $slot->getModel(), $this->getTitle() ) ) {
704  $contentHandler = $this->contentHandlerFactory
705  ->getContentHandler( $slot->getModel() );
706  $this->status = Status::newFatal( 'content-not-allowed-here',
707  ContentHandler::getLocalizedName( $contentHandler->getModelID() ),
708  $this->getTitle()->getPrefixedText(),
709  wfMessage( $roleHandler->getNameMessageKey() )
710  // TODO: defer message lookup to caller
711  );
712  return null;
713  }
714  }
715 
716  // Load the data from the master database if needed. Needed to check flags.
717  // NOTE: This grabs the parent revision as the CAS token, if grabParentRevision
718  // wasn't called yet. If the page is modified by another process before we are done with
719  // it, this method must fail (with status 'edit-conflict')!
720  // NOTE: The parent revision may be different from $this->originalRevisionId.
721  $this->grabParentRevision();
722  $flags = $this->checkFlags( $flags );
723 
724  // Avoid statsd noise and wasted cycles check the edit stash (T136678)
725  if ( ( $flags & EDIT_INTERNAL ) || ( $flags & EDIT_FORCE_BOT ) ) {
726  $useStashed = false;
727  } else {
728  $useStashed = $this->ajaxEditStash;
729  }
730 
731  $user = $this->user;
732 
733  // Prepare the update. This performs PST and generates the canonical ParserOutput.
734  $this->derivedDataUpdater->prepareContent(
735  $this->user,
736  $this->slotsUpdate,
737  $useStashed
738  );
739 
740  // TODO: don't force initialization here!
741  // This is a hack to work around the fact that late initialization of the ParserOutput
742  // causes ApiFlowEditHeaderTest::testCache to fail. Whether that failure indicates an
743  // actual problem, or is just an issue with the test setup, remains to be determined
744  // [dk, 2018-03].
745  // Anomie said in 2018-03:
746  /*
747  I suspect that what's breaking is this:
748 
749  The old version of WikiPage::doEditContent() called prepareContentForEdit() which
750  generated the ParserOutput right then, so when doEditUpdates() gets called from the
751  DeferredUpdate scheduled by WikiPage::doCreate() there's no need to parse. I note
752  there's a comment there that says "Get the pre-save transform content and final
753  parser output".
754  The new version of WikiPage::doEditContent() makes a PageUpdater and calls its
755  saveRevision(), which calls DerivedPageDataUpdater::prepareContent() and
756  PageUpdater::doCreate() without ever having to actually generate a ParserOutput.
757  Thus, when DerivedPageDataUpdater::doUpdates() is called from the DeferredUpdate
758  scheduled by PageUpdater::doCreate(), it does find that it needs to parse at that point.
759 
760  And the order of operations in that Flow test is presumably:
761 
762  - Create a page with a call to WikiPage::doEditContent(), in a way that somehow avoids
763  processing the DeferredUpdate.
764  - Set up the "no set!" mock cache in Flow\Tests\Api\ApiTestCase::expectCacheInvalidate()
765  - Then, during the course of doing that test, a $db->commit() results in the
766  DeferredUpdates being run.
767  */
768  $this->derivedDataUpdater->getCanonicalParserOutput();
769 
770  // Trigger pre-save hook (using provided edit summary)
771  $renderedRevision = $this->derivedDataUpdater->getRenderedRevision();
772  $hookStatus = Status::newGood( [] );
773  $allowedByHook = $this->hookRunner->onMultiContentSave(
774  $renderedRevision, $user, $summary, $flags, $hookStatus
775  );
776  if ( $allowedByHook && $this->hookContainer->isRegistered( 'PageContentSave' ) ) {
777  // Also run the legacy hook.
778  // NOTE: WikiPage should only be used for the legacy hook,
779  // and only if something uses the legacy hook.
780  $mainContent = $this->derivedDataUpdater->getSlots()->getContent( SlotRecord::MAIN );
781 
782  // Deprecated since 1.35.
783  $allowedByHook = $this->hookRunner->onPageContentSave(
784  $this->getWikiPage(), $user, $mainContent, $summary,
785  $flags & EDIT_MINOR, null, null, $flags, $hookStatus
786  );
787  }
788 
789  if ( !$allowedByHook ) {
790  // The hook has prevented this change from being saved.
791  if ( $hookStatus->isOK() ) {
792  // Hook returned false but didn't call fatal(); use generic message
793  $hookStatus->fatal( 'edit-hook-aborted' );
794  }
795 
796  $this->status = $hookStatus;
797  return null;
798  }
799 
800  // Provide autosummaries if one is not provided and autosummaries are enabled
801  // XXX: $summary == null seems logical, but the empty string may actually come from the user
802  // XXX: Move this logic out of the storage layer! It does not belong here! Use a callback?
803  if ( $summary->text === '' && $summary->data === null ) {
804  $summary = $this->makeAutoSummary( $flags );
805  }
806 
807  // Actually create the revision and create/update the page.
808  // Do NOT yet set $this->status!
809  if ( $flags & EDIT_UPDATE ) {
810  $status = $this->doModify( $summary, $this->user, $flags );
811  } else {
812  $status = $this->doCreate( $summary, $this->user, $flags );
813  }
814 
815  // Promote user to any groups they meet the criteria for
816  DeferredUpdates::addCallableUpdate( function () use ( $user ) {
817  $user->addAutopromoteOnceGroups( 'onEdit' );
818  $user->addAutopromoteOnceGroups( 'onView' ); // b/c
819  } );
820 
821  // NOTE: set $this->status only after all hooks have been called,
822  // so wasCommitted doesn't return true when called indirectly from a hook handler!
823  $this->status = $status;
824 
825  // TODO: replace bad status with Exceptions!
826  return ( $this->status && $this->status->isOK() )
827  ? $this->status->value['revision-record']
828  : null;
829  }
830 
836  public function wasCommitted() {
837  return $this->status !== null;
838  }
839 
863  public function getStatus() {
864  return $this->status;
865  }
866 
872  public function wasSuccessful() {
873  return $this->status && $this->status->isOK();
874  }
875 
881  public function isNew() {
882  return $this->status && $this->status->isOK() && $this->status->value['new'];
883  }
884 
892  public function isUnchanged() {
893  return $this->status
894  && $this->status->isOK()
895  && $this->status->value['revision-record'] === null;
896  }
897 
904  public function getNewRevision() {
905  return ( $this->status && $this->status->isOK() )
906  ? $this->status->value['revision-record']
907  : null;
908  }
909 
925  private function makeNewRevision(
926  CommentStoreComment $comment,
927  User $user,
928  $flags,
930  ) {
931  $wikiPage = $this->getWikiPage();
932  $title = $this->getTitle();
933  $parent = $this->grabParentRevision();
934 
935  // XXX: we expect to get a MutableRevisionRecord here, but that's a bit brittle!
936  // TODO: introduce something like an UnsavedRevisionFactory service instead!
938  $rev = $this->derivedDataUpdater->getRevision();
939  '@phan-var MutableRevisionRecord $rev';
940 
941  // Avoid fatal error when the Title's ID changed, T204793
942  if (
943  $rev->getPageId() !== null && $title->exists()
944  && $rev->getPageId() !== $title->getArticleID()
945  ) {
946  $titlePageId = $title->getArticleID();
947  $revPageId = $rev->getPageId();
948  $masterPageId = $title->getArticleID( Title::READ_LATEST );
949 
950  if ( $revPageId === $masterPageId ) {
951  wfWarn( __METHOD__ . ": Encountered stale Title object: old ID was $titlePageId, "
952  . "continuing with new ID from master, $masterPageId" );
953  } else {
954  throw new InvalidArgumentException(
955  "Revision inherited page ID $revPageId from its parent, "
956  . "but the provided Title object belongs to page ID $masterPageId"
957  );
958  }
959  }
960 
961  $rev->setPageId( $title->getArticleID() );
962 
963  if ( $parent ) {
964  $oldid = $parent->getId();
965  $rev->setParentId( $oldid );
966  } else {
967  $oldid = 0;
968  }
969 
970  $rev->setComment( $comment );
971  $rev->setUser( $user );
972  $rev->setMinorEdit( ( $flags & EDIT_MINOR ) > 0 );
973 
974  foreach ( $rev->getSlots()->getSlots() as $slot ) {
975  $content = $slot->getContent();
976 
977  // XXX: We may push this up to the "edit controller" level, see T192777.
978  // XXX: prepareSave() and isValid() could live in SlotRoleHandler
979  // XXX: PrepareSave should not take a WikiPage!
980  $prepStatus = $content->prepareSave( $wikiPage, $flags, $oldid, $user );
981 
982  // TODO: MCR: record which problem arose in which slot.
983  $status->merge( $prepStatus );
984  }
985 
986  $this->checkAllRequiredRoles(
987  $rev->getSlotRoles(),
988  $status
989  );
990 
991  return $rev;
992  }
993 
1001  private function buildEditResult( RevisionRecord $revision, bool $isNew ) {
1002  $this->editResultBuilder->setRevisionRecord( $revision );
1003  $this->editResultBuilder->setIsNew( $isNew );
1004  $this->editResult = $this->editResultBuilder->buildEditResult();
1005  }
1006 
1015  private function doModify( CommentStoreComment $summary, User $user, $flags ) {
1016  $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1017 
1018  // Update article, but only if changed.
1021  [ 'new' => false, 'revision' => null, 'revision-record' => null ],
1022  [ 'revision' => '1.35' ],
1023  __METHOD__ . ' status'
1024  )
1025  );
1026 
1027  $oldRev = $this->grabParentRevision();
1028  $oldid = $oldRev ? $oldRev->getId() : 0;
1029 
1030  if ( !$oldRev ) {
1031  // Article gone missing
1032  $status->fatal( 'edit-gone-missing' );
1033 
1034  return $status;
1035  }
1036 
1037  $newRevisionRecord = $this->makeNewRevision(
1038  $summary,
1039  $user,
1040  $flags,
1041  $status
1042  );
1043 
1044  if ( !$status->isOK() ) {
1045  return $status;
1046  }
1047 
1048  $now = $newRevisionRecord->getTimestamp();
1049 
1050  // XXX: we may want a flag that allows a null revision to be forced!
1051  $changed = $this->derivedDataUpdater->isChange();
1052 
1053  // We build the EditResult before the $change if/else branch in order to pass
1054  // the correct $newRevisionRecord to EditResultBuilder. In case this is a null
1055  // edit, $newRevisionRecord will be later overridden to its parent revision, which
1056  // would confuse EditResultBuilder.
1057  if ( !$changed ) {
1058  // This is a null edit, ensure original revision ID is set properly
1059  $this->editResultBuilder->setOriginalRevisionId( $oldid );
1060  }
1061  $this->buildEditResult( $newRevisionRecord, false );
1062 
1063  $dbw = $this->getDBConnectionRef( DB_MASTER );
1064 
1065  if ( $changed ) {
1066  $dbw->startAtomic( __METHOD__ );
1067 
1068  // Get the latest page_latest value while locking it.
1069  // Do a CAS style check to see if it's the same as when this method
1070  // started. If it changed then bail out before touching the DB.
1071  $latestNow = $wikiPage->lockAndGetLatest(); // TODO: move to storage service, pass DB
1072  if ( $latestNow != $oldid ) {
1073  // We don't need to roll back, since we did not modify the database yet.
1074  // XXX: Or do we want to rollback, any transaction started by calling
1075  // code will fail? If we want that, we should probably throw an exception.
1076  $dbw->endAtomic( __METHOD__ );
1077  // Page updated or deleted in the mean time
1078  $status->fatal( 'edit-conflict' );
1079 
1080  return $status;
1081  }
1082 
1083  // At this point we are now comitted to returning an OK
1084  // status unless some DB query error or other exception comes up.
1085  // This way callers don't have to call rollback() if $status is bad
1086  // unless they actually try to catch exceptions (which is rare).
1087 
1088  // Save revision content and meta-data
1089  $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1090 
1091  // Update page_latest and friends to reflect the new revision
1092  // TODO: move to storage service
1093  $wasRedirect = $this->derivedDataUpdater->wasRedirect();
1094  if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, null, $wasRedirect ) ) {
1095  throw new PageUpdateException( "Failed to update page row to use new revision." );
1096  }
1097 
1098  $editResult = $this->getEditResult();
1099  $tags = $this->computeEffectiveTags( $flags );
1100  $this->hookRunner->onRevisionFromEditComplete(
1101  $wikiPage, $newRevisionRecord, $editResult->getOriginalRevisionId(), $user, $tags
1102  );
1103 
1104  // Hook is hard deprecated since 1.35
1105  if ( $this->hookContainer->isRegistered( 'NewRevisionFromEditComplete' ) ) {
1106  // Only create Revision object if needed
1107  $newLegacyRevision = new Revision( $newRevisionRecord );
1108  $this->hookRunner->onNewRevisionFromEditComplete(
1109  $wikiPage,
1110  $newLegacyRevision,
1112  $user,
1113  $tags
1114  );
1115  }
1116 
1117  // Update recentchanges
1118  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1119  // Add RC row to the DB
1121  $now,
1122  $this->getTitle(),
1123  $newRevisionRecord->isMinor(),
1124  $user,
1125  $summary->text, // TODO: pass object when that becomes possible
1126  $oldid,
1127  $newRevisionRecord->getTimestamp(),
1128  ( $flags & EDIT_FORCE_BOT ) > 0,
1129  '',
1130  $oldRev->getSize(),
1131  $newRevisionRecord->getSize(),
1132  $newRevisionRecord->getId(),
1133  $this->rcPatrolStatus,
1134  $tags,
1135  $editResult
1136  );
1137  }
1138 
1139  $user->incEditCount();
1140 
1141  $dbw->endAtomic( __METHOD__ );
1142 
1143  // Return the new revision to the caller
1144  $status->value['revision-record'] = $newRevisionRecord;
1145 
1146  // Deprecated via DeprecatablePropertyArray
1147  $status->value['revision'] = function () use ( $newRevisionRecord ) {
1148  return new Revision( $newRevisionRecord );
1149  };
1150  } else {
1151  // T34948: revision ID must be set to page {{REVISIONID}} and
1152  // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
1153  // Since we don't insert a new revision into the database, the least
1154  // error-prone way is to reuse given old revision.
1155  $newRevisionRecord = $oldRev;
1156 
1157  $status->warning( 'edit-no-change' );
1158  // Update page_touched as updateRevisionOn() was not called.
1159  // Other cache updates are managed in WikiPage::onArticleEdit()
1160  // via WikiPage::doEditUpdates().
1161  $this->getTitle()->invalidateCache( $now );
1162  }
1163 
1164  // Do secondary updates once the main changes have been committed...
1165  // NOTE: the updates have to be processed before sending the response to the client
1166  // (DeferredUpdates::PRESEND), otherwise the client may already be following the
1167  // HTTP redirect to the standard view before derived data has been created - most
1168  // importantly, before the parser cache has been updated. This would cause the
1169  // content to be parsed a second time, or may cause stale content to be shown.
1171  $this->getAtomicSectionUpdate(
1172  $dbw,
1173  $wikiPage,
1174  $newRevisionRecord,
1175  $user,
1176  $summary,
1177  $flags,
1178  $status,
1179  [ 'changed' => $changed, ]
1180  ),
1182  );
1183 
1184  return $status;
1185  }
1186 
1196  private function doCreate( CommentStoreComment $summary, User $user, $flags ) {
1197  $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1198 
1199  if ( !$this->derivedDataUpdater->getSlots()->hasSlot( SlotRecord::MAIN ) ) {
1200  throw new PageUpdateException( 'Must provide a main slot when creating a page!' );
1201  }
1202 
1205  [ 'new' => true, 'revision' => null, 'revision-record' => null ],
1206  [ 'revision' => '1.35' ],
1207  __METHOD__ . ' status'
1208  )
1209  );
1210 
1211  $newRevisionRecord = $this->makeNewRevision(
1212  $summary,
1213  $user,
1214  $flags,
1215  $status
1216  );
1217 
1218  if ( !$status->isOK() ) {
1219  return $status;
1220  }
1221 
1222  $this->buildEditResult( $newRevisionRecord, true );
1223  $now = $newRevisionRecord->getTimestamp();
1224 
1225  $dbw = $this->getDBConnectionRef( DB_MASTER );
1226  $dbw->startAtomic( __METHOD__ );
1227 
1228  // Add the page record unless one already exists for the title
1229  // TODO: move to storage service
1230  $newid = $wikiPage->insertOn( $dbw );
1231  if ( $newid === false ) {
1232  $dbw->endAtomic( __METHOD__ );
1233  $status->fatal( 'edit-already-exists' );
1234 
1235  return $status;
1236  }
1237 
1238  // At this point we are now comitted to returning an OK
1239  // status unless some DB query error or other exception comes up.
1240  // This way callers don't have to call rollback() if $status is bad
1241  // unless they actually try to catch exceptions (which is rare).
1242  $newRevisionRecord->setPageId( $newid );
1243 
1244  // Save the revision text...
1245  $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1246 
1247  // Update the page record with revision data
1248  // TODO: move to storage service
1249  if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, 0 ) ) {
1250  throw new PageUpdateException( "Failed to update page row to use new revision." );
1251  }
1252 
1253  $tags = $this->computeEffectiveTags( $flags );
1254  $this->hookRunner->onRevisionFromEditComplete(
1255  $wikiPage, $newRevisionRecord, false, $user, $tags
1256  );
1257 
1258  // Hook is deprecated since 1.35
1259  if ( $this->hookContainer->isRegistered( 'NewRevisionFromEditComplete' ) ) {
1260  // ONly create Revision object if needed
1261  $newLegacyRevision = new Revision( $newRevisionRecord );
1262  $this->hookRunner->onNewRevisionFromEditComplete(
1263  $wikiPage,
1264  $newLegacyRevision,
1265  false,
1266  $user,
1267  $tags
1268  );
1269  }
1270 
1271  // Update recentchanges
1272  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1273  // Add RC row to the DB
1275  $now,
1276  $this->getTitle(),
1277  $newRevisionRecord->isMinor(),
1278  $user,
1279  $summary->text, // TODO: pass object when that becomes possible
1280  ( $flags & EDIT_FORCE_BOT ) > 0,
1281  '',
1282  $newRevisionRecord->getSize(),
1283  $newRevisionRecord->getId(),
1284  $this->rcPatrolStatus,
1285  $tags
1286  );
1287  }
1288 
1289  $user->incEditCount();
1290 
1291  if ( $this->usePageCreationLog ) {
1292  // Log the page creation
1293  // @TODO: Do we want a 'recreate' action?
1294  $logEntry = new ManualLogEntry( 'create', 'create' );
1295  $logEntry->setPerformer( $user );
1296  $logEntry->setTarget( $this->getTitle() );
1297  $logEntry->setComment( $summary->text );
1298  $logEntry->setTimestamp( $now );
1299  $logEntry->setAssociatedRevId( $newRevisionRecord->getId() );
1300  $logEntry->insert();
1301  // Note that we don't publish page creation events to recentchanges
1302  // (i.e. $logEntry->publish()) since this would create duplicate entries,
1303  // one for the edit and one for the page creation.
1304  }
1305 
1306  $dbw->endAtomic( __METHOD__ );
1307 
1308  // Return the new revision to the caller
1309  $status->value['revision-record'] = $newRevisionRecord;
1310 
1311  // Deprecated via DeprecatablePropertyArray
1312  $status->value['revision'] = function () use ( $newRevisionRecord ) {
1313  return new Revision( $newRevisionRecord );
1314  };
1315 
1316  // Do secondary updates once the main changes have been committed...
1318  $this->getAtomicSectionUpdate(
1319  $dbw,
1320  $wikiPage,
1321  $newRevisionRecord,
1322  $user,
1323  $summary,
1324  $flags,
1325  $status,
1326  [ 'created' => true ]
1327  ),
1329  );
1330 
1331  return $status;
1332  }
1333 
1334  private function getAtomicSectionUpdate(
1335  IDatabase $dbw,
1337  RevisionRecord $newRevisionRecord,
1338  User $user,
1339  CommentStoreComment $summary,
1340  $flags,
1341  Status $status,
1342  $hints = []
1343  ) {
1344  return new AtomicSectionUpdate(
1345  $dbw,
1346  __METHOD__,
1347  function () use (
1348  $wikiPage, $newRevisionRecord, $user,
1349  $summary, $flags, $status, $hints
1350  ) {
1351  // set debug data
1352  $hints['causeAction'] = 'edit-page';
1353  $hints['causeAgent'] = $user->getName();
1354 
1355  $mainContent = $newRevisionRecord->getContent( SlotRecord::MAIN, RevisionRecord::RAW );
1356  $editResult = $this->getEditResult();
1357 
1358  // Update links tables, site stats, etc.
1359  $this->derivedDataUpdater->prepareUpdate( $newRevisionRecord, $hints );
1360  $this->derivedDataUpdater->doUpdates();
1361 
1362  $created = $hints['created'] ?? false;
1363  $flags |= ( $created ? EDIT_NEW : EDIT_UPDATE );
1364 
1365  // PageSaveComplete replaces the other two since 1.35
1366  $this->hookRunner->onPageSaveComplete(
1367  $wikiPage,
1368  $user,
1369  $summary->text,
1370  $flags,
1371  $newRevisionRecord,
1372  $editResult
1373  );
1374 
1375  // Both hooks are hard deprecated since 1.35
1376  if ( !$this->hookContainer->isRegistered( 'PageContentInsertComplete' )
1377  && !$this->hookContainer->isRegistered( 'PageContentSaveComplete' )
1378  ) {
1379  // Don't go on to create a Revision unless its needed
1380  return;
1381  }
1382 
1383  $newLegacyRevision = new Revision( $newRevisionRecord );
1384  if ( $created ) {
1385  // Trigger post-create hook
1386  $this->hookRunner->onPageContentInsertComplete( $wikiPage, $user,
1387  $mainContent, $summary->text, $flags & EDIT_MINOR,
1388  null, null, $flags, $newLegacyRevision );
1389  }
1390 
1391  // Trigger post-save hook
1392  $this->hookRunner->onPageContentSaveComplete( $wikiPage, $user, $mainContent,
1393  $summary->text, $flags & EDIT_MINOR, null,
1394  null, $flags, $newLegacyRevision, $status,
1396  }
1397  );
1398  }
1399 
1403  private function getRequiredSlotRoles() {
1404  return $this->slotRoleRegistry->getRequiredRoles( $this->getTitle() );
1405  }
1406 
1410  private function getAllowedSlotRoles() {
1411  return $this->slotRoleRegistry->getAllowedRoles( $this->getTitle() );
1412  }
1413 
1414  private function ensureRoleAllowed( $role ) {
1415  $allowedRoles = $this->getAllowedSlotRoles();
1416  if ( !in_array( $role, $allowedRoles ) ) {
1417  throw new PageUpdateException( "Slot role `$role` is not allowed." );
1418  }
1419  }
1420 
1421  private function ensureRoleNotRequired( $role ) {
1422  $requiredRoles = $this->getRequiredSlotRoles();
1423  if ( in_array( $role, $requiredRoles ) ) {
1424  throw new PageUpdateException( "Slot role `$role` is required." );
1425  }
1426  }
1427 
1428  private function checkAllRolesAllowed( array $roles, Status $status ) {
1429  $allowedRoles = $this->getAllowedSlotRoles();
1430 
1431  $forbidden = array_diff( $roles, $allowedRoles );
1432  if ( !empty( $forbidden ) ) {
1433  $status->error(
1434  'edit-slots-cannot-add',
1435  count( $forbidden ),
1436  implode( ', ', $forbidden )
1437  );
1438  }
1439  }
1440 
1441  private function checkNoRolesRequired( array $roles, Status $status ) {
1442  $requiredRoles = $this->getRequiredSlotRoles();
1443 
1444  $needed = array_diff( $roles, $requiredRoles );
1445  if ( !empty( $needed ) ) {
1446  $status->error(
1447  'edit-slots-cannot-remove',
1448  count( $needed ),
1449  implode( ', ', $needed )
1450  );
1451  }
1452  }
1453 
1454  private function checkAllRequiredRoles( array $roles, Status $status ) {
1455  $requiredRoles = $this->getRequiredSlotRoles();
1456 
1457  $missing = array_diff( $requiredRoles, $roles );
1458  if ( !empty( $missing ) ) {
1459  $status->error(
1460  'edit-slots-missing',
1461  count( $missing ),
1462  implode( ', ', $missing )
1463  );
1464  }
1465  }
1466 
1467 }
MediaWiki\Storage\PageUpdater\addTags
addTags(array $tags)
Sets tags to apply to this update.
Definition: PageUpdater.php:516
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:925
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:59
CommentStoreComment\newUnsavedComment
static newUnsavedComment( $comment, array $data=null)
Create a new, unsaved CommentStoreComment.
Definition: CommentStoreComment.php:67
Revision\RevisionAccessException
Exception representing a failure to look up a revision.
Definition: RevisionAccessException.php:34
MediaWiki\Storage\PageUpdater\saveRevision
saveRevision(CommentStoreComment $summary, $flags=0)
Change an existing article or create a new article.
Definition: PageUpdater.php:669
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
MediaWiki\Storage\PageUpdater\$tags
array $tags
Definition: PageUpdater.php:158
StatusValue\newFatal
static newFatal( $message,... $parameters)
Factory function for fatal errors.
Definition: StatusValue.php:70
MediaWiki\Storage\PageUpdater\setAjaxEditStash
setAjaxEditStash( $ajaxEditStash)
Definition: PageUpdater.php:263
MediaWiki\Storage\PageUpdater\doModify
doModify(CommentStoreComment $summary, User $user, $flags)
Definition: PageUpdater.php:1015
MediaWiki\Storage\PageUpdater\$usePageCreationLog
bool $usePageCreationLog
whether to create a log entry for new page creations.
Definition: PageUpdater.php:148
WikiPage\updateRevisionOn
updateRevisionOn( $dbw, $revision, $lastRevision=null, $lastRevIsRedirect=null)
Update the page record to point to a newly saved revision.
Definition: WikiPage.php:1358
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
MediaWiki\Storage\PageUpdater\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Options that have to be present in the ServiceOptions object passed to the constructor.
Definition: PageUpdater.php:85
EDIT_FORCE_BOT
const EDIT_FORCE_BOT
Definition: Defines.php:145
MediaWiki\Storage\PageUpdater\checkNoRolesRequired
checkNoRolesRequired(array $roles, Status $status)
Definition: PageUpdater.php:1441
StatusValue\error
error( $message,... $parameters)
Add an error, do not set fatal flag This can be used for non-fatal errors.
Definition: StatusValue.php:194
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:426
User\incEditCount
incEditCount()
Schedule a deferred update to update the user's edit count.
Definition: User.php:4250
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:81
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:461
MediaWiki\Storage\PageUpdater\$ajaxEditStash
boolean $ajaxEditStash
see $wgAjaxEditStash
Definition: PageUpdater.php:153
MediaWiki\Storage\PageUpdater\getWikiId
getWikiId()
Definition: PageUpdater.php:267
RecentChange
Utility class for creating new RC entries.
Definition: RecentChange.php:73
MediaWiki\Storage\EditResult\getUndidRevId
getUndidRevId()
If the edit was an undo, returns the oldest revision that was undone.
Definition: EditResult.php:170
MediaWiki\Storage\PageUpdater\$useAutomaticEditSummaries
boolean $useAutomaticEditSummaries
see $wgUseAutomaticEditSummaries
Definition: PageUpdater.php:138
MediaWiki\Storage\PageUpdater\getExplicitTags
getExplicitTags()
Returns the list of tags set using the addTag() method.
Definition: PageUpdater.php:528
StatusValue\warning
warning( $message,... $parameters)
Add a new warning.
Definition: StatusValue.php:179
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
MediaWiki\Storage\PageUpdater\getEditResult
getEditResult()
Returns the EditResult associated with this PageUpdater.
Definition: PageUpdater.php:495
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:52
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:209
MediaWiki\Storage\PageUpdater\setUsePageCreationLog
setUsePageCreationLog( $use)
Whether to create a log entry for new page creations.
Definition: PageUpdater.php:255
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1219
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:566
MediaWiki\Storage\PageUpdater\$wikiPage
WikiPage $wikiPage
Definition: PageUpdater.php:97
MediaWiki\Storage\PageUpdater\$loadBalancer
ILoadBalancer $loadBalancer
Definition: PageUpdater.php:107
RecentChange\notifyEdit
static notifyEdit( $timestamp, $title, $minor, $user, $comment, $oldId, $lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0, $tags=[], EditResult $editResult=null)
Makes an entry in the database corresponding to an edit.
Definition: RecentChange.php:674
MediaWiki\Storage\PageUpdater\wasCommitted
wasCommitted()
Whether saveRevision() has been called on this instance.
Definition: PageUpdater.php:836
MediaWiki\Storage\PageUpdater\$editResultBuilder
EditResultBuilder $editResultBuilder
Definition: PageUpdater.php:173
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:1414
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
MediaWiki\Storage\PageUpdater\doCreate
doCreate(CommentStoreComment $summary, User $user, $flags)
Definition: PageUpdater.php:1196
Revision
Definition: Revision.php:40
MediaWiki\Storage\PageUpdater\makeAutoSummary
makeAutoSummary( $flags)
Definition: PageUpdater.php:601
MediaWiki\Storage\PageUpdater\$revisionStore
RevisionStore $revisionStore
Definition: PageUpdater.php:112
MediaWiki\Storage\EditResult\getOriginalRevisionId
getOriginalRevisionId()
Returns the ID of an earlier revision that is being repeated or restored.
Definition: EditResult.php:188
MediaWiki\Storage\PageUpdater\checkFlags
checkFlags( $flags)
Check flags and add EDIT_NEW or EDIT_UPDATE to them as needed.
Definition: PageUpdater.php:381
MediaWiki\Storage\PageUpdater\wasSuccessful
wasSuccessful()
Whether saveRevision() completed successfully.
Definition: PageUpdater.php:872
MWException
MediaWiki exception.
Definition: MWException.php:29
MediaWiki\Storage\PageUpdater\setContent
setContent( $role, Content $content)
Set the new content for the given slot role.
Definition: PageUpdater.php:395
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:25
MediaWiki\Storage\PageUpdater\markAsRevert
markAsRevert(int $revertMethod, int $oldestRevertedRevId, int $newestRevertedRevId=0)
Marks this edit as a revert and applies relevant information.
Definition: PageUpdater.php:478
MediaWiki\Storage\PageUpdater\getTitle
getTitle()
Definition: PageUpdater.php:291
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:131
WikiPage\insertOn
insertOn( $dbw, $pageId=null)
Insert a new empty page record for this article.
Definition: WikiPage.php:1313
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:904
MediaWiki\Storage\PageUpdater\computeEffectiveTags
computeEffectiveTags( $flags)
Definition: PageUpdater.php:536
MediaWiki\Storage\PageUpdater\getAtomicSectionUpdate
getAtomicSectionUpdate(IDatabase $dbw, WikiPage $wikiPage, RevisionRecord $newRevisionRecord, User $user, CommentStoreComment $summary, $flags, Status $status, $hints=[])
Definition: PageUpdater.php:1334
MediaWiki\Storage\PageUpdater\getWikiPage
getWikiPage()
Definition: PageUpdater.php:299
StatusValue\merge
merge( $other, $overwriteValue=false)
Merge another status object into this one.
Definition: StatusValue.php:224
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:77
$title
$title
Definition: testCompression.php:38
MediaWiki\Storage\PageUpdater\removeSlot
removeSlot( $role)
Removes the slot with the given role.
Definition: PageUpdater.php:444
MediaWiki\Storage\EditResultBuilder
Builder class for the EditResult object.
Definition: EditResultBuilder.php:39
DB_MASTER
const DB_MASTER
Definition: defines.php:26
MediaWiki\Storage\PageUpdater\getRequiredSlotRoles
getRequiredSlotRoles()
Definition: PageUpdater.php:1403
MediaWiki\Storage\EditResult
Object for storing information about the effects of an edit.
Definition: EditResult.php:38
WikiPage\lockAndGetLatest
lockAndGetLatest()
Lock the page row for this title+id and return page_latest (or 0)
Definition: WikiPage.php:3097
MediaWiki\Storage\PageUpdater\$hookContainer
HookContainer $hookContainer
Definition: PageUpdater.php:132
MediaWiki\Storage\PageUpdater\checkAllRolesAllowed
checkAllRolesAllowed(array $roles, Status $status)
Definition: PageUpdater.php:1428
MediaWiki\Storage\PageUpdater\addTag
addTag( $tag)
Sets a tag to apply to this update.
Definition: PageUpdater.php:505
MediaWiki\Storage\PageUpdater\getAllowedSlotRoles
getAllowedSlotRoles()
Definition: PageUpdater.php:1410
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:92
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:299
MediaWiki\Content\IContentHandlerFactory
Definition: IContentHandlerFactory.php:10
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
Revision\MutableRevisionRecord
Definition: MutableRevisionRecord.php:45
MediaWiki\Storage\PageUpdater\$contentHandlerFactory
IContentHandlerFactory $contentHandlerFactory
Definition: PageUpdater.php:122
MediaWiki\Storage\PageUpdater
Controller-like object for creating and updating pages by creating new revisions.
Definition: PageUpdater.php:78
MediaWiki\Storage\PageUpdater\getDBConnectionRef
getDBConnectionRef( $mode)
Definition: PageUpdater.php:276
MediaWiki\Storage\PageUpdater\hasEditConflict
hasEditConflict( $expectedParentRevision)
Checks whether this update conflicts with another update performed between the client loading data to...
Definition: PageUpdater.php:337
MediaWiki\Storage\PageUpdater\$hookRunner
HookRunner $hookRunner
Definition: PageUpdater.php:127
MediaWiki\Storage
Definition: BlobAccessException.php:23
Revision\SlotRecord\MAIN
const MAIN
Definition: SlotRecord.php:41
Wikimedia\Rdbms\DBUnexpectedError
@newable Stable to extend
Definition: DBUnexpectedError.php:29
Content
Base interface for content objects.
Definition: Content.php:35
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:141
MediaWiki\Storage\PageUpdater\$status
Status null $status
Definition: PageUpdater.php:168
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:163
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:371
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:749
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:863
MediaWiki\Storage\PageUpdater\setRcPatrolStatus
setRcPatrolStatus( $status)
Sets the "patrolled" status of the edit.
Definition: PageUpdater.php:244
MediaWiki\Storage\PageUpdater\$rcPatrolStatus
int $rcPatrolStatus
the RC patrol status the new revision should be marked with.
Definition: PageUpdater.php:143
RecentChange\PRC_UNPATROLLED
const PRC_UNPATROLLED
Definition: RecentChange.php:82
MediaWiki\Storage\PageUpdater\ensureRoleNotRequired
ensureRoleNotRequired( $role)
Definition: PageUpdater.php:1421
MediaWiki\Storage\PageUpdater\getContentHandler
getContentHandler( $role)
Definition: PageUpdater.php:580
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:1424
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:42
MediaWiki\Storage\PageUpdater\buildEditResult
buildEditResult(RevisionRecord $revision, bool $isNew)
Builds the EditResult for this update.
Definition: PageUpdater.php:1001
MediaWiki\Storage\PageUpdater\$derivedDataUpdater
DerivedPageDataUpdater $derivedDataUpdater
Definition: PageUpdater.php:102
MediaWiki\Storage\PageUpdater\checkAllRequiredRoles
checkAllRequiredRoles(array $roles, Status $status)
Definition: PageUpdater.php:1454
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
MediaWiki\Storage\PageUpdater\__construct
__construct(User $user, WikiPage $wikiPage, DerivedPageDataUpdater $derivedDataUpdater, ILoadBalancer $loadBalancer, RevisionStore $revisionStore, SlotRoleRegistry $slotRoleRegistry, IContentHandlerFactory $contentHandlerFactory, HookContainer $hookContainer, ServiceOptions $serviceOptions)
Definition: PageUpdater.php:191
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:1073
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:570
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:33
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\EditResult\getRevertTags
getRevertTags()
Returns an array of revert-related tags that were applied automatically to this edit.
Definition: EditResult.php:260
MediaWiki\Storage\PageUpdater\$slotRoleRegistry
SlotRoleRegistry $slotRoleRegistry
Definition: PageUpdater.php:117
User\getName
getName()
Get the user name, or the IP of an anonymous user.
Definition: User.php:1954
CommentStoreComment
Value object for a comment stored by CommentStore.
Definition: CommentStoreComment.php:30
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:231
MediaWiki\Storage\PageUpdater\$editResult
EditResult null $editResult
Definition: PageUpdater.php:178
MediaWiki\Debug\DeprecatablePropertyArray
ArrayAccess implementation that supports deprecating access to certain properties.
Definition: DeprecatablePropertyArray.php:16
MediaWiki\Storage\PageUpdater\isNew
isNew()
Whether saveRevision() was called and created a new page.
Definition: PageUpdater.php:881
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:892
MediaWiki\Storage\PageUpdater\setSlot
setSlot(SlotRecord $slot)
Set the new slot for the given slot role.
Definition: PageUpdater.php:406
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:39
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:62
MediaWiki\Storage\PageUpdater\getLinkTarget
getLinkTarget()
Definition: PageUpdater.php:283