Go to the documentation of this file.
34 use InvalidArgumentException;
60 use Wikimedia\Assert\Assert;
159 'oldrevision' =>
null,
160 'oldcountable' =>
null,
161 'oldredirect' =>
null,
162 'triggeringUser' =>
null,
165 'causeAction' =>
null,
166 'causeAgent' =>
null,
238 'knows-current' =>
true,
239 'has-content' =>
true,
240 'has-revision' =>
true,
243 'knows-current' =>
true,
244 'has-content' =>
true,
245 'has-revision' =>
true,
248 'has-content' =>
true,
249 'has-revision' =>
true,
252 'has-revision' =>
true,
305 $this->stage = $newStage;
320 if ( empty( self::$transitions[$this->stage][$newStage] ) ) {
321 throw new LogicException(
"Cannot transition from {$this->stage} to $newStage" );
354 throw new InvalidArgumentException(
'$parentId should match the parent of $revision' );
361 throw new InvalidArgumentException(
'$user should match the author of $revision' );
378 if ( $this->pageState
386 if ( $this->pageState
387 && $parentId !==
null
388 && $this->pageState[
'oldId'] !== $parentId
410 if ( $this->slotsUpdate
448 return $this->wikiPage->getTitle();
469 return $this->pageState[
'oldId'] > 0;
484 if ( $this->parentRevision ) {
488 if ( !$this->pageState[
'oldId'] ) {
494 $oldId = $this->
revision->getParentId();
495 $flags = $this->
useMaster() ? RevisionStore::READ_LATEST : 0;
496 $this->parentRevision = $oldId
497 ? $this->revisionStore->getRevisionById( $oldId, $flags )
524 if ( $this->pageState ) {
525 return $this->pageState[
'oldRevision'];
537 $current =
$rev ?
$rev->getRevisionRecord() :
null;
540 'oldRevision' => $current,
541 'oldId' =>
$rev ?
$rev->getId() : 0,
548 return $this->pageState[
'oldRevision'];
576 return $this->wikiPage->getId();
604 return $this->
getSlots()->getSlot( $role );
616 return $this->
getRawSlot( $role )->getContent();
626 return $this->
getRawSlot( $role )->getModel();
640 return $this->wikiPage->wasLoadedFrom( self::READ_LATEST );
649 if ( !$this->
getTitle()->isContentPage() ) {
665 if ( $this->articleCountMethod ===
'link' ) {
671 return $mainContent->isCountable( $hasLinks );
681 return $mainContent->isRedirect();
693 return $mainContent->isRedirect();
725 if ( $this->slotsUpdate ) {
726 if ( !$this->
user ) {
727 throw new LogicException(
728 'Unexpected state: $this->slotsUpdate was initialized, '
729 .
'but $this->user was not.'
733 if ( $this->
user->getName() !== $user->
getName() ) {
734 throw new LogicException(
'Can\'t call prepareContent() again for different user! '
735 .
'Expected ' . $this->
user->getName() .
', got ' . $user->
getName()
739 if ( !$this->slotsUpdate->hasSameUpdates(
$slotsUpdate ) ) {
740 throw new LogicException(
741 'Can\'t call prepareContent() again with different slot content!'
755 $this->slotsOutput = [];
756 $this->canonicalParserOutput =
null;
759 $stashedEdit =
false;
768 if ( $stashedEdit ) {
770 $output = $stashedEdit->output;
773 $output->setCacheTime( $stashedEdit->timestamp );
776 $this->canonicalParserOutput =
$output;
797 $oldCallback = $userPopts->getCurrentRevisionCallback();
798 $userPopts->setCurrentRevisionCallback(
802 return $legacyRevision;
804 return call_user_func( $oldCallback, $parserTitle,
$parser );
809 $pstContentSlots = $this->
revision->getSlots();
814 if ( $slot->isInherited() ) {
827 $pstContentSlots->setSlot( $pstSlot );
831 $pstContentSlots->removeSlot( $role );
840 if ( !$this->
options[
'changed'] ) {
885 if ( !$this->renderedRevision ) {
890 $this->renderedRevision = $this->revisionRenderer->getRenderedRevision(
902 if ( !$this->pageState ) {
903 throw new LogicException(
904 'Must call grabCurrentRevision() or prepareContent() '
905 .
'or prepareUpdate() before calling ' . $method
912 throw new LogicException(
913 'Must call prepareContent() or prepareUpdate() before calling ' . $method
920 throw new LogicException(
921 'Must call prepareUpdate() before calling ' . $method
933 return $this->
options[
'created'];
947 return $this->
options[
'changed'];
958 if ( $this->pageState[
'oldIsRedirect'] ===
null ) {
960 $rev = $this->pageState[
'oldRevision'];
964 $this->pageState[
'oldIsRedirect'] =
false;
968 return $this->pageState[
'oldIsRedirect'];
992 if ( !$this->slotsUpdate ) {
996 $old ? $old->getSlots() : null
1079 '$options["oldrevision"]',
1080 'must be a RevisionRecord (or Revision)'
1083 !isset(
$options[
'triggeringUser'] )
1085 '$options["triggeringUser"]',
1086 'must be a UserIdentity'
1090 throw new InvalidArgumentException(
1091 'Revision must have an ID set for it to be used with prepareUpdate()!'
1099 throw new LogicException(
1100 'Trying to re-use DerivedPageDataUpdater with revision '
1102 .
', but it\'s already bound to revision '
1111 throw new LogicException(
1112 'The Revision provided has mismatching content!'
1120 $oldId = $this->pageState[
'oldId'] ?? 0;
1121 $this->
options[
'newrev'] = ( $revision->
getId() !== $oldId );
1122 } elseif ( isset( $this->
options[
'oldrevision'] ) ) {
1124 $oldRev = $this->
options[
'oldrevision'];
1125 $oldId = $oldRev->getId();
1126 $this->
options[
'newrev'] = ( $revision->
getId() !== $oldId );
1131 if ( $oldId !==
null ) {
1140 $this->
options[
'changed'] =
true;
1143 $this->
options[
'changed'] =
false;
1146 throw new LogicException(
1147 'The Revision mismatches old revision ID: '
1148 .
'Old ID is ' . $oldId
1159 if ( $this->
user !==
null && $this->
options[
'changed'] && $this->slotsUpdate ) {
1162 throw new LogicException(
1163 'The Revision provided has a mismatching actor: expected '
1164 . $this->
user->getName()
1173 if ( !$this->pageState ) {
1174 $this->pageState = [
1175 'oldIsRedirect' => isset( $this->
options[
'oldredirect'] )
1176 && is_bool( $this->
options[
'oldredirect'] )
1177 ? $this->
options[
'oldredirect']
1179 'oldCountable' => isset( $this->
options[
'oldcountable'] )
1180 && is_bool( $this->
options[
'oldcountable'] )
1181 ? $this->
options[
'oldcountable']
1185 if ( $this->
options[
'changed'] ) {
1189 if ( isset( $this->
options[
'oldrevision'] ) ) {
1191 $this->pageState[
'oldRevision'] =
$rev instanceof
Revision
1192 ?
$rev->getRevisionRecord()
1198 $this->pageState[
'oldRevision'] =
$revision;
1204 ( $this->pageState[
'oldId'] === 0 ) );
1212 if ( !$this->
user ) {
1217 if ( $this->renderedRevision ) {
1218 $this->renderedRevision->updateRevision(
$revision );
1238 $preparedEdit->newContent =
1242 $preparedEdit->oldContent =
null;
1244 $preparedEdit->timestamp = $preparedEdit->output->getCacheTime();
1245 $preparedEdit->format = $preparedEdit->pstContent->getDefaultFormat();
1247 return $preparedEdit;
1258 [
'generate-html' => $generateHtml ]
1303 foreach ( $this->
getSlots()->getSlotRoles()
as $role ) {
1308 $updates =
$handler->getSecondaryDataUpdates(
1314 $allUpdates = array_merge( $allUpdates, $updates );
1319 $legacyUpdates =
$content->getSecondaryDataUpdates(
1327 $legacyUpdates = array_filter( $legacyUpdates,
function ( $update ) {
1331 $allUpdates = array_merge( $allUpdates, $legacyUpdates );
1349 $content = $parentSlot->getContent();
1352 $updates =
$handler->getDeletionUpdates(
1356 $allUpdates = array_merge( $allUpdates, $updates );
1362 $legacyUpdates = array_filter( $legacyUpdates,
function ( $update ) {
1366 $allUpdates = array_merge( $allUpdates, $legacyUpdates );
1371 'RevisionDataUpdates',
1402 'recursive' => $this->
options[
'changed'],
1407 if ( $this->rcWatchCategoryMembership
1410 && !$this->
options[
'restored']
1415 $this->jobQueueGroup->lazyPush(
1420 'revTimestamp' => $this->
revision->getTimestamp(),
1433 if ( mt_rand( 0, 9 ) == 0 ) {
1440 $dbKey =
$title->getPrefixedDBkey();
1441 $shortTitle =
$title->getDBkey();
1443 if ( !
$title->exists() ) {
1444 wfDebug( __METHOD__ .
": Page doesn't exist any more, bailing out\n" );
1450 if ( $this->
options[
'oldcountable'] ===
'no-change' ||
1454 } elseif ( $this->
options[
'created'] ) {
1456 } elseif ( $this->
options[
'oldcountable'] !==
null ) {
1458 - (int)$this->
options[
'oldcountable'];
1459 } elseif ( isset( $this->pageState[
'oldCountable'] ) ) {
1461 - (int)$this->pageState[
'oldCountable'];
1465 $edits = $this->
options[
'changed'] ? 1 : 0;
1466 $pages = $this->
options[
'created'] ? 1 : 0;
1469 [
'edits' => $edits,
'articles' => $good,
'pages' => $pages ]
1481 if ( $this->
options[
'changed']
1483 && $shortTitle != $legacyUser->getTitleKey()
1484 && !( $this->
revision->isMinor() && $legacyUser->isAllowed(
'nominornewtalk' ) )
1487 if ( !$recipient ) {
1488 wfDebug( __METHOD__ .
": invalid username\n" );
1496 $recipient->setNewtalk(
true, $legacyRevision );
1497 } elseif ( $recipient->isLoggedIn() ) {
1498 $recipient->setNewtalk(
true, $legacyRevision );
1500 wfDebug( __METHOD__ .
": don't need to notify a nonexistent user\n" );
1511 $this->messageCache->updateMessageOverride(
$title, $mainContent );
1515 if ( $this->
options[
'created'] ) {
1517 } elseif ( $this->
options[
'changed'] ) {
1522 $oldLegacyRevision = $oldRevision ?
new Revision( $oldRevision ) :
null;
1549 'recursive' =>
false,
1551 'transactionTicket' =>
null,
1554 if ( !in_array(
$options[
'defer'], $deferValues,
true ) ) {
1555 throw new InvalidArgumentException(
'invalid value for defer: ' .
$options[
'defer'] );
1557 Assert::parameterType(
'integer|null',
$options[
'transactionTicket'],
1558 '$options[\'transactionTicket\']' );
1563 if ( !$triggeringUser instanceof
User ) {
1566 $causeAction = $this->
options[
'causeAction'] ??
'unknown';
1567 $causeAgent = $this->
options[
'causeAgent'] ??
'unknown';
1570 if (
$options[
'defer'] ===
false &&
$options[
'transactionTicket'] !==
null ) {
1574 $this->loadbalancerFactory->commitAndWaitForReplication(
1575 __METHOD__,
$options[
'transactionTicket']
1579 foreach ( $updates
as $update ) {
1581 $update->setCause( $causeAction, $causeAgent );
1584 $update->setRevision( $legacyRevision );
1585 $update->setTriggeringUser( $triggeringUser );
1587 if (
$options[
'defer'] ===
false ) {
1588 if (
$options[
'transactionTicket'] !==
null ) {
1589 $update->setTransactionTicket(
$options[
'transactionTicket'] );
1591 $update->doUpdate();
1613 $timestamp = $this->
options[
'newrev'] ? $this->
revision->getTimestamp()
1615 $this->parserCache->save(
1617 $timestamp, $this->
revision->getId()
getCanonicalParserOutput()
Set options of the Parser.
A content handler knows how do deal with a specific type of content on a wiki page.
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
static onArticleCreate(Title $title)
The onArticle*() functions are supposed to be a kind of hooks which should be called whenever any of ...
loadPageData( $from='fromdb')
Load the object from a given source by title.
pageExisted()
Determines whether the page being edited already existed.
getParentRevision()
Returns the parent revision of the new revision wrapped by this update.
getRevision()
Returns the update's target revision - that is, the revision that will be the current revision after ...
string $articleCountMethod
see $wgArticleCountMethod
prepareContent(User $user, RevisionSlotsUpdate $slotsUpdate, $useStash=true)
Prepare updates based on an update which has not yet been saved.
getSlotParserOutput( $role, $generateHtml=true)
Job for pruning recent changes.
prepareUpdate(RevisionRecord $revision, array $options=[])
Prepare derived data updates targeting the given Revision.
setArticleCountMethod( $articleCountMethod)
getSlots()
Returns the slots of the target revision, after PST.
LBFactory $loadbalancerFactory
RevisionRecord null $parentRevision
setRcWatchCategoryMembership( $rcWatchCategoryMembership)
Abstraction for ResourceLoader modules which pull from wiki pages.
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
Class representing a MediaWiki article and history.
RevisionRenderer $revisionRenderer
Class the manages updates of *_link tables as well as similar extension-managed tables.
static newFromName( $name, $validate='valid')
Static factory method for creation from username.
getRevision()
Get the latest revision.
$options
Stores (most of) the $options parameter of prepareUpdate().
RenderedRevision $renderedRevision
static newFromIdentity(UserIdentity $identity)
Returns a User object corresponding to the given UserIdentity.
RevisionSlotsUpdate null $slotsUpdate
Interface for database access objects.
static onArticleEdit(Title $title, Revision $revision=null, $slotsChanged=null)
Purge caches on page update etc.
getCanonicalParserOptions()
doUpdates()
Do standard updates after page edit, purge, or import.
Abstract base class for update jobs that do something with some secondary data extracted from article...
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
RevisionStore $revisionStore
assertHasRevision( $method)
namespace and then decline to actually register it file or subcat img or subcat $title
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such and we might be restricted by PHP settings such as safe mode or open_basedir We cannot assume that the software even has read access anywhere useful Many shared hosts run all users web applications under the same user
isReusableFor(UserIdentity $user=null, RevisionRecord $revision=null, RevisionSlotsUpdate $slotsUpdate=null, $parentId=null)
Checks whether this DerivedPageDataUpdater can be re-used for running updates targeting the given rev...
static newFromUserAndLang(User $user, Language $lang)
Get a ParserOptions object from a given user and language.
getRevisionSlotsUpdate()
Returns the RevisionSlotsUpdate for this updater.
Class for managing the deferred updates.
getContentHandler( $role)
static isIP( $name)
Does the string match an anonymous IP address?
getContentModel( $role)
Returns the content model of the given slot.
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
see documentation in includes Linker php for Linker::makeImageLink or false for current used if you return false $parser
wasRedirect()
Whether the page was a redirect before the edit.
static factory(array $deltas)
equals(Title $title)
Compare with another title.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Class for handling updates to the site_stats table.
isCreation()
Whether the edit creates the page.
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
assertTransition( $newStage)
Asserts that a transition to the given stage is possible, without performing it.
__construct(WikiPage $wikiPage, RevisionStore $revisionStore, RevisionRenderer $revisionRenderer, ParserCache $parserCache, JobQueueGroup $jobQueueGroup, MessageCache $messageCache, Language $contLang, LBFactory $loadbalancerFactory)
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
isCountable( $editInfo=false)
Determine whether a page would be suitable for being counted as an article in the site_stats table ba...
Job to add recent change entries mentioning category membership changes.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
static array[] $transitions
Transition table for managing the life cycle of DerivedPageDateUpdater instances.
isContentDeleted()
Whether the content is deleted and thus not visible to the public.
isContentPrepared()
Whether prepareUpdate() or prepareContent() have been called on this instance.
Update object handling the cleanup of links tables after a page was deleted.
either a unescaped string or a HtmlArmor object after in associative array form externallinks $linksUpdate
getRemovedSlotRoles()
Returns the role names of the slots removed by the new revision.
array $pageState
The state of the relevant row in page table before the edit.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
MessageCache $messageCache
doTransition( $newStage)
Transition function for managing the life cycle of this instances.
getModifiedSlotRoles()
Returns the role names of the slots modified by the new revision, not including removed roles.
Base interface for content objects.
JobQueueGroup $jobQueueGroup
Represents a title within MediaWiki.
getRawContent( $role)
Returns the content of the given slot, with no audience checks.
getSecondaryDataUpdates( $recursive=false)
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
boolean $rcWatchCategoryMembership
see $wgRCWatchCategoryMembership
RevisionRecord null $revision
In both all secondary updates will be triggered handle like object that caches derived data representing a revision
getRawSlot( $role)
Returns the slot, modified or inherited, after PST, with no audience checks applied.
doSecondaryDataUpdates(array $options=[])
Do secondary data updates (such as updating link tables).
isChange()
Whether the edit created, or should create, a new revision (that is, it's not a null-edit).
assertHasPageState( $method)
Interface that deferrable updates should implement.
getTouchedSlotRoles()
Returns the role names of the slots touched by the new revision, including removed roles.
string $stage
A stage identifier for managing the life cycle of this instance.
isRedirect()
Tests if the article content represents a redirect.
Using a hook running we can avoid having all this option specific stuff in our mainline code Using the function We ve cleaned up the code here by removing clumps of infrequently used code and moving them off somewhere else It s much easier for someone working with this code to see what s _really_ going and make changes or fix bugs In we can take all the code that deals with the little used title reversing options(say) and put it in one place. Instead of having little title-reversing if-blocks spread all over the codebase in showAnArticle
A handle for managing updates for derived page data on edit, import, purge, etc.
static invalidateModuleCache(Title $title, Revision $old=null, Revision $new=null, $wikiId)
Clear the preloadTitleInfo() cache for all wiki modules on this wiki on page change if it was a JS or...
Cache of messages that are defined by MediaWiki namespace pages or by hooks.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
revisionIsRedirect(RevisionRecord $rev)
getName()
Get the user name, or the IP of an anonymous user.
Prepare an edit in shared cache so that it can be reused on edit.
static checkCache(Title $title, Content $content, User $user)
Check that a prepared edit is in cache and still up-to-date.
Internationalisation code.
grabCurrentRevision()
Returns the revision that was the page's current revision when grabCurrentRevision() was first called...
isUpdatePrepared()
Whether prepareUpdate() has been called on this instance.
Class to handle enqueueing of background jobs.
In both all secondary updates will be triggered handle like object that caches derived data representing a and can trigger updates of cached copies of that e g in the links the ParserCache