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 
71  public function import( ImportableOldRevision $importableRevision, $doUpdates = true ) {
72  $dbw = $this->loadBalancer->getConnectionRef( DB_MASTER );
73 
74  # Sneak a single revision into place
75  $user = $importableRevision->getUserObj() ?: User::newFromName( $importableRevision->getUser() );
76  if ( $user ) {
77  $userId = intval( $user->getId() );
78  $userText = $user->getName();
79  } else {
80  $userId = 0;
81  $userText = $importableRevision->getUser();
82  $user = new User;
83  }
84 
85  // avoid memory leak...?
87 
88  $page = $this->wikiPageFactory->newFromTitle( $importableRevision->getTitle() );
89  $page->loadPageData( 'fromdbmaster' );
90  if ( !$page->exists() ) {
91  // must create the page...
92  $pageId = $page->insertOn( $dbw );
93  $created = true;
94  $oldcountable = null;
95  } else {
96  $pageId = $page->getId();
97  $created = false;
98 
99  // Note: sha1 has been in XML dumps since 2012. If you have an
100  // older dump, the duplicate detection here won't work.
101  if ( $importableRevision->getSha1Base36() !== false ) {
102  $prior = $dbw->selectField( 'revision', '1',
103  [ 'rev_page' => $pageId,
104  'rev_timestamp' => $dbw->timestamp( $importableRevision->getTimestamp() ),
105  'rev_sha1' => $importableRevision->getSha1Base36() ],
106  __METHOD__
107  );
108  if ( $prior ) {
109  // @todo FIXME: This could fail slightly for multiple matches :P
110  $this->logger->debug( __METHOD__ . ": skipping existing revision for [[" .
111  $importableRevision->getTitle()->getPrefixedText() . "]], timestamp " .
112  $importableRevision->getTimestamp() . "\n" );
113  return false;
114  }
115  }
116  }
117 
118  if ( !$pageId ) {
119  // This seems to happen if two clients simultaneously try to import the
120  // same page
121  $this->logger->debug( __METHOD__ . ': got invalid $pageId when importing revision of [[' .
122  $importableRevision->getTitle()->getPrefixedText() . ']], timestamp ' .
123  $importableRevision->getTimestamp() . "\n" );
124  return false;
125  }
126 
127  // Select previous version to make size diffs correct
128  // @todo This assumes that multiple revisions of the same page are imported
129  // in order from oldest to newest.
130  $qi = $this->revisionStore->getQueryInfo();
131  $prevRevRow = $dbw->selectRow( $qi['tables'], $qi['fields'],
132  [
133  'rev_page' => $pageId,
134  'rev_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $importableRevision->getTimestamp() ) ),
135  ],
136  __METHOD__,
137  [ 'ORDER BY' => [
138  'rev_timestamp DESC',
139  'rev_id DESC', // timestamp is not unique per page
140  ]
141  ],
142  $qi['joins']
143  );
144 
145  # @todo FIXME: Use original rev_id optionally (better for backups)
146  # Insert the row
147  $revisionRecord = new MutableRevisionRecord( $importableRevision->getTitle() );
148  $revisionRecord->setParentId( $prevRevRow ? (int)$prevRevRow->rev_id : 0 );
149  $revisionRecord->setComment(
150  CommentStoreComment::newUnsavedComment( $importableRevision->getComment() )
151  );
152 
153  try {
154  $revUser = User::newFromAnyId(
155  $userId,
156  $userText,
157  null
158  );
159  } catch ( InvalidArgumentException $ex ) {
160  $revUser = RequestContext::getMain()->getUser();
161  }
162  $revisionRecord->setUser( $revUser );
163 
164  $originalRevision = $prevRevRow
165  ? $this->revisionStore->newRevisionFromRow(
166  $prevRevRow,
167  IDBAccessObject::READ_LATEST,
168  $importableRevision->getTitle()
169  )
170  : null;
171 
172  foreach ( $importableRevision->getSlotRoles() as $role ) {
173  if ( !$this->slotRoleRegistry->isDefinedRole( $role ) ) {
174  throw new MWException( "Undefined slot role $role" );
175  }
176 
177  $newContent = $importableRevision->getContent( $role );
178  if ( !$originalRevision || !$originalRevision->hasSlot( $role ) ) {
179  $revisionRecord->setContent( $role, $newContent );
180  } else {
181  $originalSlot = $originalRevision->getSlot( $role );
182  if ( !$originalSlot->hasSameContent( $importableRevision->getSlot( $role ) ) ) {
183  $revisionRecord->setContent( $role, $newContent );
184  } else {
185  $revisionRecord->inheritSlot( $originalRevision->getSlot( $role ) );
186  }
187  }
188  }
189 
190  $revisionRecord->setTimestamp( $importableRevision->getTimestamp() );
191  $revisionRecord->setMinorEdit( $importableRevision->getMinor() );
192  $revisionRecord->setPageId( $pageId );
193 
194  $latestRevId = $page->getLatest();
195 
196  $inserted = $this->revisionStore->insertRevisionOn( $revisionRecord, $dbw );
197  if ( $latestRevId ) {
198  // If not found (false), cast to 0 so that the page is updated
199  // Just to be on the safe side, even though it should always be found
200  $latestRevTimestamp = (int)$this->revisionStore->getTimestampFromId(
201  $latestRevId,
202  RevisionStore::READ_LATEST
203  );
204  } else {
205  $latestRevTimestamp = 0;
206  }
207  if ( $importableRevision->getTimestamp() > $latestRevTimestamp ) {
208  $changed = $page->updateRevisionOn( $dbw, $inserted, $latestRevId );
209  } else {
210  $changed = false;
211  }
212 
213  $tags = $importableRevision->getTags();
214  if ( $tags !== [] ) {
215  ChangeTags::addTags( $tags, null, $inserted->getId() );
216  }
217 
218  if ( $changed !== false && $this->doUpdates ) {
219  $this->logger->debug( __METHOD__ . ": running updates" );
220  // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
221  // @todo replace deprecated function
222  $page->doEditUpdates(
223  $inserted,
224  $user,
225  [ 'created' => $created, 'oldcountable' => 'no-change' ]
226  );
227  }
228 
229  return true;
230  }
231 
232 }
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:166
Title\clearCaches
static clearCaches()
Definition: Title.php:3323
Revision\RevisionStore
Service for looking up page revisions.
Definition: RevisionStore.php:80
true
return true
Definition: router.php:90
ImportableOldRevision\getTimestamp
getTimestamp()
User\newFromName
static newFromName( $name, $validate='valid')
Definition: User.php:558
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:19
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:656
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:476
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
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:63
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:308
ImportableOldRevision\getUserObj
getUserObj()