MediaWiki  master
ImportableOldRevisionImporter.php
Go to the documentation of this file.
1 <?php
2 
8 use Psr\Log\LoggerInterface;
10 
15 
19  private $logger;
20 
24  private $doUpdates;
25 
29  private $loadBalancer;
30 
34  private $revisionStore;
35 
40 
45 
54  public function __construct(
55  $doUpdates,
56  LoggerInterface $logger,
61  ) {
62  $this->doUpdates = $doUpdates;
63  $this->logger = $logger;
64  $this->loadBalancer = $loadBalancer;
65  $this->revisionStore = $revisionStore;
66  $this->slotRoleRegistry = $slotRoleRegistry;
67  // @todo: temporary - remove when FileImporter extension is updated
68  $this->wikiPageFactory = $wikiPageFactory ?? MediaWikiServices::getInstance()->getWikiPageFactory();
69  }
70 
72  public function import( ImportableOldRevision $importableRevision, $doUpdates = true ) {
73  $dbw = $this->loadBalancer->getConnectionRef( DB_PRIMARY );
74 
75  # Sneak a single revision into place
76  $user = $importableRevision->getUserObj() ?: User::newFromName( $importableRevision->getUser() );
77  if ( $user ) {
78  $userId = $user->getId();
79  $userText = $user->getName();
80  } else {
81  $userId = 0;
82  $userText = $importableRevision->getUser();
83  $user = new User;
84  }
85 
86  // avoid memory leak...?
88 
89  $page = $this->wikiPageFactory->newFromTitle( $importableRevision->getTitle() );
90  $page->loadPageData( 'fromdbmaster' );
91  if ( !$page->exists() ) {
92  // must create the page...
93  $pageId = $page->insertOn( $dbw );
94  $created = true;
95  $oldcountable = null;
96  } else {
97  $pageId = $page->getId();
98  $created = false;
99 
100  // Note: sha1 has been in XML dumps since 2012. If you have an
101  // older dump, the duplicate detection here won't work.
102  if ( $importableRevision->getSha1Base36() !== false ) {
103  $prior = (bool)$dbw->selectField( 'revision', '1',
104  [ 'rev_page' => $pageId,
105  'rev_timestamp' => $dbw->timestamp( $importableRevision->getTimestamp() ),
106  'rev_sha1' => $importableRevision->getSha1Base36() ],
107  __METHOD__
108  );
109  if ( $prior ) {
110  // @todo FIXME: This could fail slightly for multiple matches :P
111  $this->logger->debug( __METHOD__ . ": skipping existing revision for [[" .
112  $importableRevision->getTitle()->getPrefixedText() . "]], timestamp " .
113  $importableRevision->getTimestamp() . "\n" );
114  return false;
115  }
116  }
117  }
118 
119  if ( !$pageId ) {
120  // This seems to happen if two clients simultaneously try to import the
121  // same page
122  $this->logger->debug( __METHOD__ . ': got invalid $pageId when importing revision of [[' .
123  $importableRevision->getTitle()->getPrefixedText() . ']], timestamp ' .
124  $importableRevision->getTimestamp() . "\n" );
125  return false;
126  }
127 
128  // Select previous version to make size diffs correct
129  // @todo This assumes that multiple revisions of the same page are imported
130  // in order from oldest to newest.
131  $qi = $this->revisionStore->getQueryInfo();
132  $prevRevRow = $dbw->selectRow( $qi['tables'], $qi['fields'],
133  [
134  'rev_page' => $pageId,
135  'rev_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $importableRevision->getTimestamp() ) ),
136  ],
137  __METHOD__,
138  [ 'ORDER BY' => [
139  'rev_timestamp DESC',
140  'rev_id DESC', // timestamp is not unique per page
141  ]
142  ],
143  $qi['joins']
144  );
145 
146  # @todo FIXME: Use original rev_id optionally (better for backups)
147  # Insert the row
148  $revisionRecord = new MutableRevisionRecord( $importableRevision->getTitle() );
149  $revisionRecord->setParentId( $prevRevRow ? (int)$prevRevRow->rev_id : 0 );
150  $revisionRecord->setComment(
151  CommentStoreComment::newUnsavedComment( $importableRevision->getComment() )
152  );
153 
154  try {
155  $revUser = User::newFromAnyId(
156  $userId,
157  $userText,
158  null
159  );
160  } catch ( InvalidArgumentException $ex ) {
161  $revUser = RequestContext::getMain()->getUser();
162  }
163  $revisionRecord->setUser( $revUser );
164 
165  $originalRevision = $prevRevRow
166  ? $this->revisionStore->newRevisionFromRow(
167  $prevRevRow,
168  IDBAccessObject::READ_LATEST,
169  $importableRevision->getTitle()
170  )
171  : null;
172 
173  foreach ( $importableRevision->getSlotRoles() as $role ) {
174  if ( !$this->slotRoleRegistry->isDefinedRole( $role ) ) {
175  throw new MWException( "Undefined slot role $role" );
176  }
177 
178  $newContent = $importableRevision->getContent( $role );
179  if ( !$originalRevision || !$originalRevision->hasSlot( $role ) ) {
180  $revisionRecord->setContent( $role, $newContent );
181  } else {
182  $originalSlot = $originalRevision->getSlot( $role );
183  if ( !$originalSlot->hasSameContent( $importableRevision->getSlot( $role ) ) ) {
184  $revisionRecord->setContent( $role, $newContent );
185  } else {
186  $revisionRecord->inheritSlot( $originalRevision->getSlot( $role ) );
187  }
188  }
189  }
190 
191  $revisionRecord->setTimestamp( $importableRevision->getTimestamp() );
192  $revisionRecord->setMinorEdit( $importableRevision->getMinor() );
193  $revisionRecord->setPageId( $pageId );
194 
195  $latestRevId = $page->getLatest();
196 
197  $inserted = $this->revisionStore->insertRevisionOn( $revisionRecord, $dbw );
198  if ( $latestRevId ) {
199  // If not found (false), cast to 0 so that the page is updated
200  // Just to be on the safe side, even though it should always be found
201  $latestRevTimestamp = (int)$this->revisionStore->getTimestampFromId(
202  $latestRevId,
203  RevisionStore::READ_LATEST
204  );
205  } else {
206  $latestRevTimestamp = 0;
207  }
208  if ( $importableRevision->getTimestamp() > $latestRevTimestamp ) {
209  $changed = $page->updateRevisionOn( $dbw, $inserted, $latestRevId );
210  } else {
211  $changed = false;
212  }
213 
214  $tags = $importableRevision->getTags();
215  if ( $tags !== [] ) {
216  ChangeTags::addTags( $tags, null, $inserted->getId() );
217  }
218 
219  if ( $changed !== false && $this->doUpdates ) {
220  $this->logger->debug( __METHOD__ . ": running updates" );
221  // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
222  // @todo replace deprecated function
223  $page->doEditUpdates(
224  $inserted,
225  $user,
226  [ 'created' => $created, 'oldcountable' => 'no-change' ]
227  );
228  }
229 
230  return true;
231  }
232 
233 }
CommentStoreComment\newUnsavedComment
static newUnsavedComment( $comment, array $data=null)
Create a new, unsaved CommentStoreComment.
Definition: CommentStoreComment.php:67
ImportableOldRevisionImporter\$doUpdates
bool $doUpdates
Definition: ImportableOldRevisionImporter.php:24
ImportableOldRevisionImporter
Definition: ImportableOldRevisionImporter.php:14
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:191
Title\clearCaches
static clearCaches()
Definition: Title.php:3039
MediaWiki\Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:88
true
return true
Definition: router.php:90
ImportableOldRevision\getTimestamp
getTimestamp()
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:606
ImportableOldRevisionImporter\$logger
LoggerInterface $logger
Definition: ImportableOldRevisionImporter.php:19
ImportableOldRevisionImporter\$slotRoleRegistry
SlotRoleRegistry $slotRoleRegistry
Definition: ImportableOldRevisionImporter.php:39
ImportableOldRevision\getUser
getUser()
ImportableOldRevision\getMinor
getMinor()
MWException
MediaWiki exception.
Definition: MWException.php:29
ImportableOldRevision\getTags
getTags()
ImportableOldRevision\getSha1Base36
getSha1Base36()
ImportableOldRevision\getContent
getContent( $role=SlotRecord::MAIN)
Page\WikiPageFactory
Definition: WikiPageFactory.php:20
User\newFromAnyId
static newFromAnyId( $userId, $userName, $actorId, $dbDomain=false)
Static factory method for creation from an ID, name, and/or actor ID.
Definition: User.php:712
ImportableOldRevision
Definition: ImportableOldRevision.php:8
MediaWiki\Revision\MutableRevisionRecord
Definition: MutableRevisionRecord.php:44
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
ImportableOldRevision\getTitle
getTitle()
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:484
ImportableOldRevisionImporter\$loadBalancer
ILoadBalancer $loadBalancer
Definition: ImportableOldRevisionImporter.php:29
ImportableOldRevision\getComment
getComment()
OldRevisionImporter
Definition: OldRevisionImporter.php:6
ImportableOldRevision\getSlot
getSlot( $role)
ImportableOldRevision\getSlotRoles
getSlotRoles()
ImportableOldRevisionImporter\$revisionStore
RevisionStore $revisionStore
Definition: ImportableOldRevisionImporter.php:34
MediaWiki\Revision\SlotRoleRegistry
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
Definition: SlotRoleRegistry.php:48
ImportableOldRevisionImporter\$wikiPageFactory
WikiPageFactory $wikiPageFactory
Definition: ImportableOldRevisionImporter.php:44
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:68
ImportableOldRevisionImporter\__construct
__construct( $doUpdates, LoggerInterface $logger, ILoadBalancer $loadBalancer, RevisionStore $revisionStore, SlotRoleRegistry $slotRoleRegistry, WikiPageFactory $wikiPageFactory=null)
Definition: ImportableOldRevisionImporter.php:54
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
ChangeTags\addTags
static addTags( $tags, $rc_id=null, $rev_id=null, $log_id=null, $params=null, RecentChange $rc=null)
Add tags to a change given its rc_id, rev_id and/or log_id.
Definition: ChangeTags.php:328
ImportableOldRevision\getUserObj
getUserObj()