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