MediaWiki  master
PageUpdater.php
Go to the documentation of this file.
1 <?php
25 namespace MediaWiki\Storage;
26 
29 use Content;
30 use ContentHandler;
31 use DeferredUpdates;
32 use InvalidArgumentException;
33 use LogicException;
34 use ManualLogEntry;
47 use MWException;
48 use RecentChange;
49 use Revision;
50 use RuntimeException;
51 use Status;
52 use Title;
53 use User;
54 use Wikimedia\Assert\Assert;
59 use WikiPage;
60 
77 class PageUpdater {
78 
84  public const CONSTRUCTOR_OPTIONS = [
85  'ManualRevertSearchRadius',
86  'UseRCPatrol',
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 
183  private $softwareTags;
184 
189 
203  public function __construct(
204  User $user,
213  array $softwareTags
214  ) {
215  $serviceOptions->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
216  $this->serviceOptions = $serviceOptions;
217 
218  $this->user = $user;
219  $this->wikiPage = $wikiPage;
220  $this->derivedDataUpdater = $derivedDataUpdater;
221 
222  $this->loadBalancer = $loadBalancer;
223  $this->revisionStore = $revisionStore;
224  $this->slotRoleRegistry = $slotRoleRegistry;
225  $this->contentHandlerFactory = $contentHandlerFactory;
226  $this->hookContainer = $hookContainer;
227  $this->hookRunner = new HookRunner( $hookContainer );
228  $this->softwareTags = $softwareTags;
229 
230  $this->slotsUpdate = new RevisionSlotsUpdate();
231  $this->editResultBuilder = new EditResultBuilder(
235  new ServiceOptions(
237  [
238  'ManualRevertSearchRadius' =>
239  $serviceOptions->get( 'ManualRevertSearchRadius' )
240  ]
241  )
242  );
243  }
244 
253  $this->useAutomaticEditSummaries = $useAutomaticEditSummaries;
254  }
255 
265  public function setRcPatrolStatus( $status ) {
266  $this->rcPatrolStatus = $status;
267  }
268 
276  public function setUsePageCreationLog( $use ) {
277  $this->usePageCreationLog = $use;
278  }
279 
284  public function setAjaxEditStash( $ajaxEditStash ) {
285  $this->ajaxEditStash = $ajaxEditStash;
286  }
287 
288  private function getWikiId() {
289  return false; // TODO: get from RevisionStore!
290  }
291 
297  private function getDBConnectionRef( $mode ) {
298  return $this->loadBalancer->getConnectionRef( $mode, [], $this->getWikiId() );
299  }
300 
304  private function getLinkTarget() {
305  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
306  return $this->wikiPage->getTitle();
307  }
308 
312  private function getTitle() {
313  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
314  return $this->wikiPage->getTitle();
315  }
316 
320  private function getWikiPage() {
321  // NOTE: eventually, we won't get a WikiPage passed into the constructor any more
322  return $this->wikiPage;
323  }
324 
358  public function hasEditConflict( $expectedParentRevision ) {
359  $parent = $this->grabParentRevision();
360  $parentId = $parent ? $parent->getId() : 0;
361 
362  return $parentId !== $expectedParentRevision;
363  }
364 
392  public function grabParentRevision() {
393  return $this->derivedDataUpdater->grabCurrentRevision();
394  }
395 
402  private function checkFlags( $flags ) {
403  if ( !( $flags & EDIT_NEW ) && !( $flags & EDIT_UPDATE ) ) {
404  $flags |= ( $this->derivedDataUpdater->pageExisted() ) ? EDIT_UPDATE : EDIT_NEW;
405  }
406 
407  return $flags;
408  }
409 
416  public function setContent( $role, Content $content ) {
417  $this->ensureRoleAllowed( $role );
418 
419  $this->slotsUpdate->modifyContent( $role, $content );
420  }
421 
427  public function setSlot( SlotRecord $slot ) {
428  $this->ensureRoleAllowed( $slot->getRole() );
429 
430  $this->slotsUpdate->modifySlot( $slot );
431  }
432 
447  public function inheritSlot( SlotRecord $originalSlot ) {
448  // NOTE: slots can be inherited even if the role is not "allowed" on the title.
449  // NOTE: this slot is inherited from some other revision, but it's
450  // a "modified" slot for the RevisionSlotsUpdate and DerivedPageDataUpdater,
451  // since it's not implicitly inherited from the parent revision.
452  $inheritedSlot = SlotRecord::newInherited( $originalSlot );
453  $this->slotsUpdate->modifySlot( $inheritedSlot );
454  }
455 
465  public function removeSlot( $role ) {
466  $this->ensureRoleNotRequired( $role );
467 
468  $this->slotsUpdate->removeSlot( $role );
469  }
470 
482  public function setOriginalRevisionId( $originalRevId ) {
483  $this->editResultBuilder->setOriginalRevisionId( $originalRevId );
484  }
485 
499  public function markAsRevert(
500  int $revertMethod,
501  int $oldestRevertedRevId,
502  int $newestRevertedRevId = 0
503  ) {
504  $this->editResultBuilder->markAsRevert(
505  $revertMethod, $oldestRevertedRevId, $newestRevertedRevId
506  );
507  }
508 
516  public function getEditResult() : ?EditResult {
517  return $this->editResult;
518  }
519 
526  public function addTag( $tag ) {
527  Assert::parameterType( 'string', $tag, '$tag' );
528  $this->tags[] = trim( $tag );
529  }
530 
537  public function addTags( array $tags ) {
538  Assert::parameterElementType( 'string', $tags, '$tags' );
539  foreach ( $tags as $tag ) {
540  $this->addTag( $tag );
541  }
542  }
543 
549  public function getExplicitTags() {
550  return $this->tags;
551  }
552 
557  private function computeEffectiveTags( $flags ) {
558  $tags = $this->tags;
559  $editResult = $this->getEditResult();
560 
561  foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
562  $old_content = $this->getParentContent( $role );
563 
564  $handler = $this->getContentHandler( $role );
565  $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
566 
567  // TODO: MCR: Do this for all slots. Also add tags for removing roles!
568  $tag = $handler->getChangeTag( $old_content, $content, $flags );
569  // If there is no applicable tag, null is returned, so we need to check
570  if ( $tag ) {
571  $tags[] = $tag;
572  }
573  }
574 
575  $tags = array_merge( $tags, $editResult->getRevertTags() );
576 
577  return array_unique( $tags );
578  }
579 
587  private function getParentContent( $role ) {
588  $parent = $this->grabParentRevision();
589 
590  if ( $parent && $parent->hasSlot( $role ) ) {
591  return $parent->getContent( $role, RevisionRecord::RAW );
592  }
593 
594  return null;
595  }
596 
601  private function getContentHandler( $role ) {
602  if ( $this->slotsUpdate->isModifiedSlot( $role ) ) {
603  $slot = $this->slotsUpdate->getModifiedSlot( $role );
604  } else {
605  $parent = $this->grabParentRevision();
606 
607  if ( $parent ) {
608  $slot = $parent->getSlot( $role, RevisionRecord::RAW );
609  } else {
610  throw new RevisionAccessException( 'No such slot: ' . $role );
611  }
612  }
613 
614  return $this->contentHandlerFactory->getContentHandler( $slot->getModel() );
615  }
616 
622  private function makeAutoSummary( $flags ) {
623  if ( !$this->useAutomaticEditSummaries || ( $flags & EDIT_AUTOSUMMARY ) === 0 ) {
625  }
626 
627  // NOTE: this generates an auto-summary for SOME RANDOM changed slot!
628  // TODO: combine auto-summaries for multiple slots!
629  // XXX: this logic should not be in the storage layer!
630  $roles = $this->slotsUpdate->getModifiedRoles();
631  $role = reset( $roles );
632 
633  if ( $role === false ) {
635  }
636 
637  $handler = $this->getContentHandler( $role );
638  $content = $this->slotsUpdate->getModifiedSlot( $role )->getContent();
639  $old_content = $this->getParentContent( $role );
640  $summary = $handler->getAutosummary( $old_content, $content, $flags );
641 
642  return CommentStoreComment::newUnsavedComment( $summary );
643  }
644 
690  public function saveRevision( CommentStoreComment $summary, $flags = 0 ) {
691  // Defend against mistakes caused by differences with the
692  // signature of WikiPage::doEditContent.
693  Assert::parameterType( 'integer', $flags, '$flags' );
694 
695  if ( $this->wasCommitted() ) {
696  throw new RuntimeException( 'saveRevision() has already been called on this PageUpdater!' );
697  }
698 
699  // Low-level sanity check
700  if ( $this->getLinkTarget()->getText() === '' ) {
701  throw new RuntimeException( 'Something is trying to edit an article with an empty title' );
702  }
703 
704  // NOTE: slots can be inherited even if the role is not "allowed" on the title.
706  $this->checkAllRolesAllowed(
707  $this->slotsUpdate->getModifiedRoles(),
708  $status
709  );
710  $this->checkNoRolesRequired(
711  $this->slotsUpdate->getRemovedRoles(),
712  $status
713  );
714 
715  if ( !$status->isOK() ) {
716  return null;
717  }
718 
719  // Make sure the given content is allowed in the respective slots of this page
720  foreach ( $this->slotsUpdate->getModifiedRoles() as $role ) {
721  $slot = $this->slotsUpdate->getModifiedSlot( $role );
722  $roleHandler = $this->slotRoleRegistry->getRoleHandler( $role );
723 
724  if ( !$roleHandler->isAllowedModel( $slot->getModel(), $this->getTitle() ) ) {
725  $contentHandler = $this->contentHandlerFactory
726  ->getContentHandler( $slot->getModel() );
727  $this->status = Status::newFatal( 'content-not-allowed-here',
728  ContentHandler::getLocalizedName( $contentHandler->getModelID() ),
729  $this->getTitle()->getPrefixedText(),
730  wfMessage( $roleHandler->getNameMessageKey() )
731  // TODO: defer message lookup to caller
732  );
733  return null;
734  }
735  }
736 
737  // Load the data from the master database if needed. Needed to check flags.
738  // NOTE: This grabs the parent revision as the CAS token, if grabParentRevision
739  // wasn't called yet. If the page is modified by another process before we are done with
740  // it, this method must fail (with status 'edit-conflict')!
741  // NOTE: The parent revision may be different from $this->originalRevisionId.
742  $this->grabParentRevision();
743  $flags = $this->checkFlags( $flags );
744 
745  // Avoid statsd noise and wasted cycles check the edit stash (T136678)
746  if ( ( $flags & EDIT_INTERNAL ) || ( $flags & EDIT_FORCE_BOT ) ) {
747  $useStashed = false;
748  } else {
749  $useStashed = $this->ajaxEditStash;
750  }
751 
752  $user = $this->user;
753 
754  // Prepare the update. This performs PST and generates the canonical ParserOutput.
755  $this->derivedDataUpdater->prepareContent(
756  $this->user,
757  $this->slotsUpdate,
758  $useStashed
759  );
760 
761  // Trigger pre-save hook (using provided edit summary)
762  $renderedRevision = $this->derivedDataUpdater->getRenderedRevision();
763  $hookStatus = Status::newGood( [] );
764  $allowedByHook = $this->hookRunner->onMultiContentSave(
765  $renderedRevision, $user, $summary, $flags, $hookStatus
766  );
767  if ( $allowedByHook && $this->hookContainer->isRegistered( 'PageContentSave' ) ) {
768  // Also run the legacy hook.
769  // NOTE: WikiPage should only be used for the legacy hook,
770  // and only if something uses the legacy hook.
771  $mainContent = $this->derivedDataUpdater->getSlots()->getContent( SlotRecord::MAIN );
772 
773  // Deprecated since 1.35.
774  $allowedByHook = $this->hookRunner->onPageContentSave(
775  $this->getWikiPage(), $user, $mainContent, $summary,
776  $flags & EDIT_MINOR, null, null, $flags, $hookStatus
777  );
778  }
779 
780  if ( !$allowedByHook ) {
781  // The hook has prevented this change from being saved.
782  if ( $hookStatus->isOK() ) {
783  // Hook returned false but didn't call fatal(); use generic message
784  $hookStatus->fatal( 'edit-hook-aborted' );
785  }
786 
787  $this->status = $hookStatus;
788  return null;
789  }
790 
791  // Provide autosummaries if one is not provided and autosummaries are enabled
792  // XXX: $summary == null seems logical, but the empty string may actually come from the user
793  // XXX: Move this logic out of the storage layer! It does not belong here! Use a callback?
794  if ( $summary->text === '' && $summary->data === null ) {
795  $summary = $this->makeAutoSummary( $flags );
796  }
797 
798  // Actually create the revision and create/update the page.
799  // Do NOT yet set $this->status!
800  if ( $flags & EDIT_UPDATE ) {
801  $status = $this->doModify( $summary, $this->user, $flags );
802  } else {
803  $status = $this->doCreate( $summary, $this->user, $flags );
804  }
805 
806  // Promote user to any groups they meet the criteria for
807  DeferredUpdates::addCallableUpdate( function () use ( $user ) {
808  $user->addAutopromoteOnceGroups( 'onEdit' );
809  $user->addAutopromoteOnceGroups( 'onView' ); // b/c
810  } );
811 
812  // NOTE: set $this->status only after all hooks have been called,
813  // so wasCommitted doesn't return true when called indirectly from a hook handler!
814  $this->status = $status;
815 
816  // TODO: replace bad status with Exceptions!
817  return ( $this->status && $this->status->isOK() )
818  ? $this->status->value['revision-record']
819  : null;
820  }
821 
827  public function wasCommitted() {
828  return $this->status !== null;
829  }
830 
854  public function getStatus() {
855  return $this->status;
856  }
857 
863  public function wasSuccessful() {
864  return $this->status && $this->status->isOK();
865  }
866 
872  public function isNew() {
873  return $this->status && $this->status->isOK() && $this->status->value['new'];
874  }
875 
883  public function isUnchanged() {
884  return $this->status
885  && $this->status->isOK()
886  && $this->status->value['revision-record'] === null;
887  }
888 
895  public function getNewRevision() {
896  return ( $this->status && $this->status->isOK() )
897  ? $this->status->value['revision-record']
898  : null;
899  }
900 
916  private function makeNewRevision(
917  CommentStoreComment $comment,
918  User $user,
919  $flags,
921  ) {
922  $wikiPage = $this->getWikiPage();
923  $title = $this->getTitle();
924  $parent = $this->grabParentRevision();
925 
926  // XXX: we expect to get a MutableRevisionRecord here, but that's a bit brittle!
927  // TODO: introduce something like an UnsavedRevisionFactory service instead!
929  $rev = $this->derivedDataUpdater->getRevision();
930  '@phan-var MutableRevisionRecord $rev';
931 
932  // Avoid fatal error when the Title's ID changed, T204793
933  if (
934  $rev->getPageId() !== null && $title->exists()
935  && $rev->getPageId() !== $title->getArticleID()
936  ) {
937  $titlePageId = $title->getArticleID();
938  $revPageId = $rev->getPageId();
939  $masterPageId = $title->getArticleID( Title::READ_LATEST );
940 
941  if ( $revPageId === $masterPageId ) {
942  wfWarn( __METHOD__ . ": Encountered stale Title object: old ID was $titlePageId, "
943  . "continuing with new ID from master, $masterPageId" );
944  } else {
945  throw new InvalidArgumentException(
946  "Revision inherited page ID $revPageId from its parent, "
947  . "but the provided Title object belongs to page ID $masterPageId"
948  );
949  }
950  }
951 
952  $rev->setPageId( $title->getArticleID() );
953 
954  if ( $parent ) {
955  $oldid = $parent->getId();
956  $rev->setParentId( $oldid );
957  } else {
958  $oldid = 0;
959  }
960 
961  $rev->setComment( $comment );
962  $rev->setUser( $user );
963  $rev->setMinorEdit( ( $flags & EDIT_MINOR ) > 0 );
964 
965  foreach ( $rev->getSlots()->getSlots() as $slot ) {
966  $content = $slot->getContent();
967 
968  // XXX: We may push this up to the "edit controller" level, see T192777.
969  // XXX: prepareSave() and isValid() could live in SlotRoleHandler
970  // XXX: PrepareSave should not take a WikiPage!
971  $prepStatus = $content->prepareSave( $wikiPage, $flags, $oldid, $user );
972 
973  // TODO: MCR: record which problem arose in which slot.
974  $status->merge( $prepStatus );
975  }
976 
977  $this->checkAllRequiredRoles(
978  $rev->getSlotRoles(),
979  $status
980  );
981 
982  return $rev;
983  }
984 
992  private function buildEditResult( RevisionRecord $revision, bool $isNew ) {
993  $this->editResultBuilder->setRevisionRecord( $revision );
994  $this->editResultBuilder->setIsNew( $isNew );
995  $this->editResult = $this->editResultBuilder->buildEditResult();
996  }
997 
1006  private function doModify( CommentStoreComment $summary, User $user, $flags ) {
1007  $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1008 
1009  // Update article, but only if changed.
1012  [ 'new' => false, 'revision' => null, 'revision-record' => null ],
1013  [ 'revision' => '1.35' ],
1014  __METHOD__ . ' status'
1015  )
1016  );
1017 
1018  $oldRev = $this->grabParentRevision();
1019  $oldid = $oldRev ? $oldRev->getId() : 0;
1020 
1021  if ( !$oldRev ) {
1022  // Article gone missing
1023  $status->fatal( 'edit-gone-missing' );
1024 
1025  return $status;
1026  }
1027 
1028  $newRevisionRecord = $this->makeNewRevision(
1029  $summary,
1030  $user,
1031  $flags,
1032  $status
1033  );
1034 
1035  if ( !$status->isOK() ) {
1036  return $status;
1037  }
1038 
1039  $now = $newRevisionRecord->getTimestamp();
1040 
1041  // XXX: we may want a flag that allows a null revision to be forced!
1042  $changed = $this->derivedDataUpdater->isChange();
1043 
1044  // We build the EditResult before the $change if/else branch in order to pass
1045  // the correct $newRevisionRecord to EditResultBuilder. In case this is a null
1046  // edit, $newRevisionRecord will be later overridden to its parent revision, which
1047  // would confuse EditResultBuilder.
1048  if ( !$changed ) {
1049  // This is a null edit, ensure original revision ID is set properly
1050  $this->editResultBuilder->setOriginalRevisionId( $oldid );
1051  }
1052  $this->buildEditResult( $newRevisionRecord, false );
1053 
1054  $dbw = $this->getDBConnectionRef( DB_MASTER );
1055 
1056  if ( $changed ) {
1057  $dbw->startAtomic( __METHOD__ );
1058 
1059  // Get the latest page_latest value while locking it.
1060  // Do a CAS style check to see if it's the same as when this method
1061  // started. If it changed then bail out before touching the DB.
1062  $latestNow = $wikiPage->lockAndGetLatest(); // TODO: move to storage service, pass DB
1063  if ( $latestNow != $oldid ) {
1064  // We don't need to roll back, since we did not modify the database yet.
1065  // XXX: Or do we want to rollback, any transaction started by calling
1066  // code will fail? If we want that, we should probably throw an exception.
1067  $dbw->endAtomic( __METHOD__ );
1068  // Page updated or deleted in the mean time
1069  $status->fatal( 'edit-conflict' );
1070 
1071  return $status;
1072  }
1073 
1074  // At this point we are now comitted to returning an OK
1075  // status unless some DB query error or other exception comes up.
1076  // This way callers don't have to call rollback() if $status is bad
1077  // unless they actually try to catch exceptions (which is rare).
1078 
1079  // Save revision content and meta-data
1080  $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1081 
1082  // Update page_latest and friends to reflect the new revision
1083  // TODO: move to storage service
1084  $wasRedirect = $this->derivedDataUpdater->wasRedirect();
1085  if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, null, $wasRedirect ) ) {
1086  throw new PageUpdateException( "Failed to update page row to use new revision." );
1087  }
1088 
1089  $editResult = $this->getEditResult();
1090  $tags = $this->computeEffectiveTags( $flags );
1091  $this->hookRunner->onRevisionFromEditComplete(
1092  $wikiPage, $newRevisionRecord, $editResult->getOriginalRevisionId(), $user, $tags
1093  );
1094 
1095  // Hook is hard deprecated since 1.35
1096  if ( $this->hookContainer->isRegistered( 'NewRevisionFromEditComplete' ) ) {
1097  // Only create Revision object if needed
1098  $newLegacyRevision = new Revision( $newRevisionRecord );
1099  $this->hookRunner->onNewRevisionFromEditComplete(
1100  $wikiPage,
1101  $newLegacyRevision,
1103  $user,
1104  $tags
1105  );
1106  }
1107 
1108  // Update recentchanges
1109  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1110  // Add RC row to the DB
1112  $now,
1113  $this->getTitle(),
1114  $newRevisionRecord->isMinor(),
1115  $user,
1116  $summary->text, // TODO: pass object when that becomes possible
1117  $oldid,
1118  $newRevisionRecord->getTimestamp(),
1119  ( $flags & EDIT_FORCE_BOT ) > 0,
1120  '',
1121  $oldRev->getSize(),
1122  $newRevisionRecord->getSize(),
1123  $newRevisionRecord->getId(),
1124  $this->rcPatrolStatus,
1125  $tags,
1126  $editResult
1127  );
1128  }
1129 
1130  $user->incEditCount();
1131 
1132  $dbw->endAtomic( __METHOD__ );
1133 
1134  // Return the new revision to the caller
1135  $status->value['revision-record'] = $newRevisionRecord;
1136 
1137  // Deprecated via DeprecatablePropertyArray
1138  $status->value['revision'] = function () use ( $newRevisionRecord ) {
1139  return new Revision( $newRevisionRecord );
1140  };
1141  } else {
1142  // T34948: revision ID must be set to page {{REVISIONID}} and
1143  // related variables correctly. Likewise for {{REVISIONUSER}} (T135261).
1144  // Since we don't insert a new revision into the database, the least
1145  // error-prone way is to reuse given old revision.
1146  $newRevisionRecord = $oldRev;
1147 
1148  $status->warning( 'edit-no-change' );
1149  // Update page_touched as updateRevisionOn() was not called.
1150  // Other cache updates are managed in WikiPage::onArticleEdit()
1151  // via WikiPage::doEditUpdates().
1152  $this->getTitle()->invalidateCache( $now );
1153  }
1154 
1155  // Do secondary updates once the main changes have been committed...
1156  // NOTE: the updates have to be processed before sending the response to the client
1157  // (DeferredUpdates::PRESEND), otherwise the client may already be following the
1158  // HTTP redirect to the standard view before derived data has been created - most
1159  // importantly, before the parser cache has been updated. This would cause the
1160  // content to be parsed a second time, or may cause stale content to be shown.
1162  $this->getAtomicSectionUpdate(
1163  $dbw,
1164  $wikiPage,
1165  $newRevisionRecord,
1166  $user,
1167  $summary,
1168  $flags,
1169  $status,
1170  [ 'changed' => $changed, ]
1171  ),
1172  DeferredUpdates::PRESEND
1173  );
1174 
1175  return $status;
1176  }
1177 
1187  private function doCreate( CommentStoreComment $summary, User $user, $flags ) {
1188  $wikiPage = $this->getWikiPage(); // TODO: use for legacy hooks only!
1189 
1190  if ( !$this->derivedDataUpdater->getSlots()->hasSlot( SlotRecord::MAIN ) ) {
1191  throw new PageUpdateException( 'Must provide a main slot when creating a page!' );
1192  }
1193 
1196  [ 'new' => true, 'revision' => null, 'revision-record' => null ],
1197  [ 'revision' => '1.35' ],
1198  __METHOD__ . ' status'
1199  )
1200  );
1201 
1202  $newRevisionRecord = $this->makeNewRevision(
1203  $summary,
1204  $user,
1205  $flags,
1206  $status
1207  );
1208 
1209  if ( !$status->isOK() ) {
1210  return $status;
1211  }
1212 
1213  $this->buildEditResult( $newRevisionRecord, true );
1214  $now = $newRevisionRecord->getTimestamp();
1215 
1216  $dbw = $this->getDBConnectionRef( DB_MASTER );
1217  $dbw->startAtomic( __METHOD__ );
1218 
1219  // Add the page record unless one already exists for the title
1220  // TODO: move to storage service
1221  $newid = $wikiPage->insertOn( $dbw );
1222  if ( $newid === false ) {
1223  $dbw->endAtomic( __METHOD__ );
1224  $status->fatal( 'edit-already-exists' );
1225 
1226  return $status;
1227  }
1228 
1229  // At this point we are now comitted to returning an OK
1230  // status unless some DB query error or other exception comes up.
1231  // This way callers don't have to call rollback() if $status is bad
1232  // unless they actually try to catch exceptions (which is rare).
1233  $newRevisionRecord->setPageId( $newid );
1234 
1235  // Save the revision text...
1236  $newRevisionRecord = $this->revisionStore->insertRevisionOn( $newRevisionRecord, $dbw );
1237 
1238  // Update the page record with revision data
1239  // TODO: move to storage service
1240  if ( !$wikiPage->updateRevisionOn( $dbw, $newRevisionRecord, 0 ) ) {
1241  throw new PageUpdateException( "Failed to update page row to use new revision." );
1242  }
1243 
1244  $tags = $this->computeEffectiveTags( $flags );
1245  $this->hookRunner->onRevisionFromEditComplete(
1246  $wikiPage, $newRevisionRecord, false, $user, $tags
1247  );
1248 
1249  // Hook is deprecated since 1.35
1250  if ( $this->hookContainer->isRegistered( 'NewRevisionFromEditComplete' ) ) {
1251  // ONly create Revision object if needed
1252  $newLegacyRevision = new Revision( $newRevisionRecord );
1253  $this->hookRunner->onNewRevisionFromEditComplete(
1254  $wikiPage,
1255  $newLegacyRevision,
1256  false,
1257  $user,
1258  $tags
1259  );
1260  }
1261 
1262  // Update recentchanges
1263  if ( !( $flags & EDIT_SUPPRESS_RC ) ) {
1264  // Add RC row to the DB
1266  $now,
1267  $this->getTitle(),
1268  $newRevisionRecord->isMinor(),
1269  $user,
1270  $summary->text, // TODO: pass object when that becomes possible
1271  ( $flags & EDIT_FORCE_BOT ) > 0,
1272  '',
1273  $newRevisionRecord->getSize(),
1274  $newRevisionRecord->getId(),
1275  $this->rcPatrolStatus,
1276  $tags
1277  );
1278  }
1279 
1280  $user->incEditCount();
1281 
1282  if ( $this->usePageCreationLog ) {
1283  // Log the page creation
1284  // @TODO: Do we want a 'recreate' action?
1285  $logEntry = new ManualLogEntry( 'create', 'create' );
1286  $logEntry->setPerformer( $user );
1287  $logEntry->setTarget( $this->getTitle() );
1288  $logEntry->setComment( $summary->text );
1289  $logEntry->setTimestamp( $now );
1290  $logEntry->setAssociatedRevId( $newRevisionRecord->getId() );
1291  $logEntry->insert();
1292  // Note that we don't publish page creation events to recentchanges
1293  // (i.e. $logEntry->publish()) since this would create duplicate entries,
1294  // one for the edit and one for the page creation.
1295  }
1296 
1297  $dbw->endAtomic( __METHOD__ );
1298 
1299  // Return the new revision to the caller
1300  $status->value['revision-record'] = $newRevisionRecord;
1301 
1302  // Deprecated via DeprecatablePropertyArray
1303  $status->value['revision'] = function () use ( $newRevisionRecord ) {
1304  return new Revision( $newRevisionRecord );
1305  };
1306 
1307  // Do secondary updates once the main changes have been committed...
1309  $this->getAtomicSectionUpdate(
1310  $dbw,
1311  $wikiPage,
1312  $newRevisionRecord,
1313  $user,
1314  $summary,
1315  $flags,
1316  $status,
1317  [ 'created' => true ]
1318  ),
1319  DeferredUpdates::PRESEND
1320  );
1321 
1322  return $status;
1323  }
1324 
1325  private function getAtomicSectionUpdate(
1326  IDatabase $dbw,
1328  RevisionRecord $newRevisionRecord,
1329  User $user,
1330  CommentStoreComment $summary,
1331  $flags,
1332  Status $status,
1333  $hints = []
1334  ) {
1335  return new AtomicSectionUpdate(
1336  $dbw,
1337  __METHOD__,
1338  function () use (
1339  $wikiPage, $newRevisionRecord, $user,
1340  $summary, $flags, $status, $hints
1341  ) {
1342  // set debug data
1343  $hints['causeAction'] = 'edit-page';
1344  $hints['causeAgent'] = $user->getName();
1345 
1346  $editResult = $this->getEditResult();
1347  $hints['editResult'] = $editResult;
1348 
1349  if ( $editResult->isRevert() ) {
1350  // Should the reverted tag update be scheduled right away?
1351  // The revert is approved if either patrolling is disabled or the
1352  // edit is patrolled or autopatrolled.
1353  $approved = !$this->serviceOptions->get( 'UseRCPatrol' ) ||
1354  $this->rcPatrolStatus === RecentChange::PRC_PATROLLED ||
1355  $this->rcPatrolStatus === RecentChange::PRC_AUTOPATROLLED;
1356 
1357  // Allow extensions to override the patrolling subsystem.
1358  $this->hookRunner->onBeforeRevertedTagUpdate(
1359  $wikiPage,
1360  $user,
1361  $summary,
1362  $flags,
1363  $newRevisionRecord,
1364  $editResult,
1365  $approved
1366  );
1367  $hints['approved'] = $approved;
1368  }
1369 
1370  // Update links tables, site stats, etc.
1371  $this->derivedDataUpdater->prepareUpdate( $newRevisionRecord, $hints );
1372  $this->derivedDataUpdater->doUpdates();
1373 
1374  $created = $hints['created'] ?? false;
1375  $flags |= ( $created ? EDIT_NEW : EDIT_UPDATE );
1376 
1377  // PageSaveComplete replaces the other two since 1.35
1378  $this->hookRunner->onPageSaveComplete(
1379  $wikiPage,
1380  $user,
1381  $summary->text,
1382  $flags,
1383  $newRevisionRecord,
1384  $editResult
1385  );
1386 
1387  // Both hooks are hard deprecated since 1.35
1388  if ( !$this->hookContainer->isRegistered( 'PageContentInsertComplete' )
1389  && !$this->hookContainer->isRegistered( 'PageContentSaveComplete' )
1390  ) {
1391  // Don't go on to create a Revision unless its needed
1392  return;
1393  }
1394 
1395  $mainContent = $newRevisionRecord->getContent( SlotRecord::MAIN, RevisionRecord::RAW );
1396  $newLegacyRevision = new Revision( $newRevisionRecord );
1397  if ( $created ) {
1398  // Trigger post-create hook
1399  $this->hookRunner->onPageContentInsertComplete( $wikiPage, $user,
1400  $mainContent, $summary->text, $flags & EDIT_MINOR,
1401  null, null, $flags, $newLegacyRevision );
1402  }
1403 
1404  // Trigger post-save hook
1405  $this->hookRunner->onPageContentSaveComplete( $wikiPage, $user, $mainContent,
1406  $summary->text, $flags & EDIT_MINOR, null,
1407  null, $flags, $newLegacyRevision, $status,
1409  }
1410  );
1411  }
1412 
1416  private function getRequiredSlotRoles() {
1417  return $this->slotRoleRegistry->getRequiredRoles( $this->getTitle() );
1418  }
1419 
1423  private function getAllowedSlotRoles() {
1424  return $this->slotRoleRegistry->getAllowedRoles( $this->getTitle() );
1425  }
1426 
1427  private function ensureRoleAllowed( $role ) {
1428  $allowedRoles = $this->getAllowedSlotRoles();
1429  if ( !in_array( $role, $allowedRoles ) ) {
1430  throw new PageUpdateException( "Slot role `$role` is not allowed." );
1431  }
1432  }
1433 
1434  private function ensureRoleNotRequired( $role ) {
1435  $requiredRoles = $this->getRequiredSlotRoles();
1436  if ( in_array( $role, $requiredRoles ) ) {
1437  throw new PageUpdateException( "Slot role `$role` is required." );
1438  }
1439  }
1440 
1441  private function checkAllRolesAllowed( array $roles, Status $status ) {
1442  $allowedRoles = $this->getAllowedSlotRoles();
1443 
1444  $forbidden = array_diff( $roles, $allowedRoles );
1445  if ( !empty( $forbidden ) ) {
1446  $status->error(
1447  'edit-slots-cannot-add',
1448  count( $forbidden ),
1449  implode( ', ', $forbidden )
1450  );
1451  }
1452  }
1453 
1454  private function checkNoRolesRequired( array $roles, Status $status ) {
1455  $requiredRoles = $this->getRequiredSlotRoles();
1456 
1457  $needed = array_diff( $roles, $requiredRoles );
1458  if ( !empty( $needed ) ) {
1459  $status->error(
1460  'edit-slots-cannot-remove',
1461  count( $needed ),
1462  implode( ', ', $needed )
1463  );
1464  }
1465  }
1466 
1467  private function checkAllRequiredRoles( array $roles, Status $status ) {
1468  $requiredRoles = $this->getRequiredSlotRoles();
1469 
1470  $missing = array_diff( $requiredRoles, $roles );
1471  if ( !empty( $missing ) ) {
1472  $status->error(
1473  'edit-slots-missing',
1474  count( $missing ),
1475  implode( ', ', $missing )
1476  );
1477  }
1478  }
1479 
1480 }
MediaWiki\Storage\PageUpdater\addTags
addTags(array $tags)
Sets tags to apply to this update.
Definition: PageUpdater.php:537
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:916
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:59
EDIT_AUTOSUMMARY
const EDIT_AUTOSUMMARY
Definition: Defines.php:141
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:690
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:45
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:284
MediaWiki\Storage\PageUpdater\doModify
doModify(CommentStoreComment $summary, User $user, $flags)
Definition: PageUpdater.php:1006
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:1360
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:105
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:84
MediaWiki\Storage\PageUpdater\checkNoRolesRequired
checkNoRolesRequired(array $roles, Status $status)
Definition: PageUpdater.php:1454
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
MediaWiki\Storage\PageUpdater\inheritSlot
inheritSlot(SlotRecord $originalSlot)
Explicitly inherit a slot from some earlier revision.
Definition: PageUpdater.php:447
User\incEditCount
incEditCount()
Schedule a deferred update to update the user's edit count.
Definition: User.php:4177
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:482
MediaWiki\Storage\PageUpdater\getWikiId
getWikiId()
Definition: PageUpdater.php:288
RecentChange
Utility class for creating new RC entries.
Definition: RecentChange.php:74
MediaWiki\Storage\EditResult\getUndidRevId
getUndidRevId()
If the edit was an undo, returns the oldest revision that was undone.
Definition: EditResult.php:173
MediaWiki\Storage\PageUpdater\getExplicitTags
getExplicitTags()
Returns the list of tags set using the addTag() method.
Definition: PageUpdater.php:549
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 pending update queue for execution at the appropriate time.
Definition: DeferredUpdates.php:119
MediaWiki\Storage\PageUpdater\getEditResult
getEditResult()
Returns the EditResult associated with this PageUpdater.
Definition: PageUpdater.php:516
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:55
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:276
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1230
MediaWiki\Storage\PageUpdater\$ajaxEditStash
bool $ajaxEditStash
see $wgAjaxEditStash
Definition: PageUpdater.php:153
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:587
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:656
MediaWiki\Storage\PageUpdater\wasCommitted
wasCommitted()
Whether saveRevision() has been called on this instance.
Definition: PageUpdater.php:827
MediaWiki\Storage\EditResultBuilder\CONSTRUCTOR_OPTIONS
const CONSTRUCTOR_OPTIONS
Definition: EditResultBuilder.php:41
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:1427
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:1187
Revision
Definition: Revision.php:40
MediaWiki\Storage\PageUpdater\makeAutoSummary
makeAutoSummary( $flags)
Definition: PageUpdater.php:622
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:191
MediaWiki\Storage\PageUpdater\checkFlags
checkFlags( $flags)
Check flags and add EDIT_NEW or EDIT_UPDATE to them as needed.
Definition: PageUpdater.php:402
MediaWiki\Storage\PageUpdater\wasSuccessful
wasSuccessful()
Whether saveRevision() completed successfully.
Definition: PageUpdater.php:863
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:416
MediaWiki\Config\ServiceOptions
A class for passing options to services.
Definition: ServiceOptions.php:27
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:499
MediaWiki\Storage\PageUpdater\__construct
__construct(User $user, WikiPage $wikiPage, DerivedPageDataUpdater $derivedDataUpdater, ILoadBalancer $loadBalancer, RevisionStore $revisionStore, SlotRoleRegistry $slotRoleRegistry, IContentHandlerFactory $contentHandlerFactory, HookContainer $hookContainer, ServiceOptions $serviceOptions, array $softwareTags)
Definition: PageUpdater.php:203
MediaWiki\Storage\PageUpdater\getTitle
getTitle()
Definition: PageUpdater.php:312
Revision\SlotRecord\getRole
getRole()
Returns the role of the slot.
Definition: SlotRecord.php:482
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:1315
DeferredUpdates
Class for managing the deferral of updates within the scope of a PHP script invocation.
Definition: DeferredUpdates.php:82
MediaWiki\Storage\PageUpdater\getNewRevision
getNewRevision()
The new revision created by saveRevision(), or null if saveRevision() has not yet been called,...
Definition: PageUpdater.php:895
MediaWiki\Storage\PageUpdater\computeEffectiveTags
computeEffectiveTags( $flags)
Definition: PageUpdater.php:557
MediaWiki\Storage\PageUpdater\getAtomicSectionUpdate
getAtomicSectionUpdate(IDatabase $dbw, WikiPage $wikiPage, RevisionRecord $newRevisionRecord, User $user, CommentStoreComment $summary, $flags, Status $status, $hints=[])
Definition: PageUpdater.php:1325
MediaWiki\Storage\PageUpdater\getWikiPage
getWikiPage()
Definition: PageUpdater.php:320
StatusValue\merge
merge( $other, $overwriteValue=false)
Merge another status object into this one.
Definition: StatusValue.php:220
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:135
Revision\RevisionRecord\RAW
const RAW
Definition: RevisionRecord.php:60
$title
$title
Definition: testCompression.php:38
MediaWiki\Storage\PageUpdater\removeSlot
removeSlot( $role)
Removes the slot with the given role.
Definition: PageUpdater.php:465
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\$serviceOptions
ServiceOptions $serviceOptions
Definition: PageUpdater.php:188
MediaWiki\Storage\PageUpdater\getRequiredSlotRoles
getRequiredSlotRoles()
Definition: PageUpdater.php:1416
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:3109
MediaWiki\Storage\PageUpdater\$hookContainer
HookContainer $hookContainer
Definition: PageUpdater.php:132
MediaWiki\Storage\PageUpdater\checkAllRolesAllowed
checkAllRolesAllowed(array $roles, Status $status)
Definition: PageUpdater.php:1441
MediaWiki\Storage\PageUpdater\addTag
addTag( $tag)
Sets a tag to apply to this update.
Definition: PageUpdater.php:526
RecentChange\PRC_PATROLLED
const PRC_PATROLLED
Definition: RecentChange.php:84
MediaWiki\Storage\PageUpdater\getAllowedSlotRoles
getAllowedSlotRoles()
Definition: PageUpdater.php:1423
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
MediaWiki\Storage\PageUpdater\$softwareTags
string[] $softwareTags
currently enabled software change tags
Definition: PageUpdater.php:183
$content
$content
Definition: router.php:76
MediaWiki\Storage\PageUpdater\$user
User $user
Definition: PageUpdater.php:92
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:77
MediaWiki\Storage\PageUpdater\getDBConnectionRef
getDBConnectionRef( $mode)
Definition: PageUpdater.php:297
MediaWiki\Storage\PageUpdater\$useAutomaticEditSummaries
bool $useAutomaticEditSummaries
see $wgUseAutomaticEditSummaries
Definition: PageUpdater.php:138
MediaWiki\Storage\PageUpdater\hasEditConflict
hasEditConflict( $expectedParentRevision)
Checks whether this update conflicts with another update performed between the client loading data to...
Definition: PageUpdater.php:358
EDIT_SUPPRESS_RC
const EDIT_SUPPRESS_RC
Definition: Defines.php:138
RecentChange\PRC_AUTOPATROLLED
const PRC_AUTOPATROLLED
Definition: RecentChange.php:85
MediaWiki\Storage\PageUpdater\$hookRunner
HookRunner $hookRunner
Definition: PageUpdater.php:127
MediaWiki\Storage
Definition: BlobAccessException.php:23
Revision\SlotRecord\MAIN
const MAIN
Definition: SlotRecord.php:43
Wikimedia\Rdbms\DBUnexpectedError
@newable Stable to extend
Definition: DBUnexpectedError.php:29
EDIT_UPDATE
const EDIT_UPDATE
Definition: Defines.php:136
Content
Base interface for content objects.
Definition: Content.php:35
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:46
MediaWiki\Storage\PageUpdater\grabParentRevision
grabParentRevision()
Returns the revision that was the page's current revision when grabParentRevision() was first called.
Definition: PageUpdater.php:392
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:731
MediaWiki\Storage\PageUpdater\getStatus
getStatus()
The Status object indicating whether saveRevision() was successful, or null if saveRevision() was not...
Definition: PageUpdater.php:854
MediaWiki\Storage\PageUpdater\setRcPatrolStatus
setRcPatrolStatus( $status)
Sets the "patrolled" status of the edit.
Definition: PageUpdater.php:265
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:83
MediaWiki\Storage\EditResult\isRevert
isRevert()
Whether the edit was a revert, not necessarily exact.
Definition: EditResult.php:221
MediaWiki\Storage\PageUpdater\ensureRoleNotRequired
ensureRoleNotRequired( $role)
Definition: PageUpdater.php:1434
MediaWiki\Storage\PageUpdater\getContentHandler
getContentHandler( $role)
Definition: PageUpdater.php:601
User\addAutopromoteOnceGroups
addAutopromoteOnceGroups( $event)
Add the user to the group if he/she meets given criteria.
Definition: User.php:1446
ManualLogEntry
Class for creating new log entries and inserting them into the database.
Definition: ManualLogEntry.php:43
MediaWiki\Storage\PageUpdater\buildEditResult
buildEditResult(RevisionRecord $revision, bool $isNew)
Builds the EditResult for this update.
Definition: PageUpdater.php:992
MediaWiki\Storage\PageUpdater\$derivedDataUpdater
DerivedPageDataUpdater $derivedDataUpdater
Definition: PageUpdater.php:102
MediaWiki\Storage\PageUpdater\checkAllRequiredRoles
checkAllRequiredRoles(array $roles, Status $status)
Definition: PageUpdater.php:1467
Revision\SlotRoleRegistry
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
Definition: SlotRoleRegistry.php:48
MediaWiki\Config\ServiceOptions\get
get( $key)
Definition: ServiceOptions.php:88
MediaWiki\HookContainer\HookContainer
HookContainer class.
Definition: HookContainer.php:45
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:1080
MediaWiki\HookContainer\HookRunner
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Definition: HookRunner.php:571
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
EDIT_FORCE_BOT
const EDIT_FORCE_BOT
Definition: Defines.php:139
EDIT_MINOR
const EDIT_MINOR
Definition: Defines.php:137
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:104
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:63
DeferredUpdates\addCallableUpdate
static addCallableUpdate( $callable, $stage=self::POSTSEND, $dbw=null)
Add an update to the pending update queue that invokes the specified callback when run.
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:264
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:2057
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:252
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:872
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
Revision\RevisionRecord\getContent
getContent( $role, $audience=self::FOR_PUBLIC, Authority $performer=null)
Returns the Content of the given slot of this revision.
Definition: RevisionRecord.php:152
MediaWiki\Storage\PageUpdater\isUnchanged
isUnchanged()
Whether saveRevision() did not create a revision because the content didn't change (null-edit).
Definition: PageUpdater.php:883
MediaWiki\Storage\PageUpdater\setSlot
setSlot(SlotRecord $slot)
Set the new slot for the given slot role.
Definition: PageUpdater.php:427
Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
MediaWiki\Config\ServiceOptions\assertRequiredOptions
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
Definition: ServiceOptions.php:66
MediaWiki\Storage\PageUpdater\getLinkTarget
getLinkTarget()
Definition: PageUpdater.php:304
EDIT_INTERNAL
const EDIT_INTERNAL
Definition: Defines.php:142