43 parent::__construct( $mainModule, $moduleName, $modulePrefix );
44 $this->revisionStore = MediaWikiServices::getInstance()->getRevisionStore();
45 $this->slotRoleRegistry = MediaWikiServices::getInstance()->getSlotRoleRegistry();
53 $params,
'fromtitle',
'fromid',
'fromrev',
'fromtext',
'fromslots'
56 $params,
'totitle',
'toid',
'torev',
'totext',
'torelative',
'toslots'
59 $this->props = array_flip( $params[
'prop'] );
62 $this->
getMain()->setCacheMode(
'public' );
65 list( $fromRev, $fromRelRev, $fromValsRev ) = $this->
getDiffRevision(
'from', $params );
68 if ( $params[
'torelative'] !==
null ) {
70 $this->
dieWithError(
'apierror-compare-relative-to-nothing' );
75 $this->
dieWithError( [
'apierror-compare-relative-to-deleted', $params[
'torelative'] ] );
77 switch ( $params[
'torelative'] ) {
80 list( $toRev, $toRelRev, $toValsRev ) = [ $fromRev, $fromRelRev, $fromValsRev ];
81 $fromRev = $this->revisionStore->getPreviousRevision( $toRelRev );
82 $fromRelRev = $fromRev;
83 $fromValsRev = $fromRev;
87 'apiwarn-compare-no-prev',
94 $fromRev = $this->revisionStore->newMutableRevisionFromArray( [
99 $toRelRev->getContent( SlotRecord::MAIN, RevisionRecord::RAW )
100 ->getContentHandler()
107 $toRev = $this->revisionStore->getNextRevision( $fromRelRev );
113 'apiwarn-compare-no-next',
120 $toRev = MutableRevisionRecord::newFromParentRevision( $fromRelRev );
125 $title = $fromRelRev->getPageAsLinkTarget();
126 $toRev = $this->revisionStore->getRevisionByTitle(
$title );
138 list( $toRev, $toRelRev, $toValsRev ) = $this->
getDiffRevision(
'to', $params );
143 if ( !$fromRev || !$toRev ) {
149 if ( !$fromRev->audienceCan(
150 RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_THIS_USER, $this->getUser()
152 $this->
dieWithError( [
'apierror-missingcontent-revid', $fromRev->getId() ],
'missingcontent' );
154 if ( !$toRev->audienceCan(
155 RevisionRecord::DELETED_TEXT, RevisionRecord::FOR_THIS_USER, $this->getUser()
157 $this->
dieWithError( [
'apierror-missingcontent-revid', $toRev->getId() ],
'missingcontent' );
162 if ( $fromRelRev && $fromRelRev->getPageAsLinkTarget() ) {
164 } elseif ( $toRelRev && $toRelRev->getPageAsLinkTarget() ) {
173 $de->setRevisions( $fromRev, $toRev );
174 if ( $params[
'slots'] ===
null ) {
175 $difftext = $de->getDiffBody();
176 if ( $difftext ===
false ) {
181 foreach ( $params[
'slots'] as $role ) {
182 $difftext[$role] = $de->getDiffBodyForRole( $role );
188 $this->
setVals( $vals,
'from', $fromValsRev );
189 $this->
setVals( $vals,
'to', $toValsRev );
191 if ( isset( $this->props[
'rel'] ) ) {
193 $rev = $this->revisionStore->getPreviousRevision( $fromRev );
195 $vals[
'prev'] = $rev->getId();
199 $rev = $this->revisionStore->getNextRevision( $toRev );
201 $vals[
'next'] = $rev->getId();
206 if ( isset( $this->props[
'diffsize'] ) ) {
207 $vals[
'diffsize'] = 0;
208 foreach ( (array)$difftext as $text ) {
209 $vals[
'diffsize'] += strlen( $text );
212 if ( isset( $this->props[
'diff'] ) ) {
213 if ( is_array( $difftext ) ) {
215 $vals[
'bodies'] = $difftext;
236 $rev = $this->revisionStore->getRevisionById( $id );
238 ->userHasAnyRight( $this->
getUser(),
'deletedtext',
'undelete' )
241 $arQuery = $this->revisionStore->getArchiveQueryInfo();
242 $row = $this->
getDB()->selectRow(
246 [
'ar_namespace',
'ar_title' ]
248 [
'ar_rev_id' => $id ],
254 $rev = $this->revisionStore->newRevisionFromArchiveRow( $row );
256 $rev->isArchive =
true;
268 if ( $this->guessedTitle !==
false ) {
272 $this->guessedTitle =
null;
275 foreach ( [
'from',
'to' ] as $prefix ) {
276 if ( $params[
"{$prefix}rev"] !==
null ) {
284 if ( $params[
"{$prefix}title"] !==
null ) {
287 $this->guessedTitle =
$title;
292 if ( $params[
"{$prefix}id"] !==
null ) {
295 $this->guessedTitle =
$title;
313 foreach ( [
'from',
'to' ] as $prefix ) {
314 if ( $params[
"{$prefix}rev"] !==
null ) {
316 if ( $rev && $rev->hasSlot( $role ) ) {
317 return $rev->getSlot( $role, RevisionRecord::RAW )->getModel();
324 return $this->slotRoleRegistry->getRoleHandler( $role )->getDefaultModel(
$guessedTitle );
327 if ( isset( $params[
"fromcontentmodel-$role"] ) ) {
328 return $params[
"fromcontentmodel-$role"];
330 if ( isset( $params[
"tocontentmodel-$role"] ) ) {
331 return $params[
"tocontentmodel-$role"];
334 if ( $role === SlotRecord::MAIN ) {
335 if ( isset( $params[
'fromcontentmodel'] ) ) {
336 return $params[
'fromcontentmodel'];
338 if ( isset( $params[
'tocontentmodel'] ) ) {
339 return $params[
'tocontentmodel'];
365 if ( $params[
"{$prefix}text"] !==
null ) {
366 $params[
"{$prefix}slots"] = [ SlotRecord::MAIN ];
367 $params[
"{$prefix}text-main"] = $params[
"{$prefix}text"];
368 $params[
"{$prefix}section-main"] =
null;
369 $params[
"{$prefix}contentmodel-main"] = $params[
"{$prefix}contentmodel"];
370 $params[
"{$prefix}contentformat-main"] = $params[
"{$prefix}contentformat"];
375 $suppliedContent = $params[
"{$prefix}slots"] !==
null;
379 if ( $params[
"{$prefix}rev"] !==
null ) {
380 $revId = $params[
"{$prefix}rev"];
381 } elseif ( $params[
"{$prefix}title"] !==
null || $params[
"{$prefix}id"] !==
null ) {
382 if ( $params[
"{$prefix}title"] !==
null ) {
392 $this->
dieWithError( [
'apierror-nosuchpageid', $params[
"{$prefix}id"] ] );
395 $revId =
$title->getLatestRevID();
399 if ( !$suppliedContent ) {
413 if ( $revId !==
null ) {
416 $this->
dieWithError( [
'apierror-nosuchrevid', $revId ] );
422 if ( !$suppliedContent ) {
426 if ( isset( $params[
"{$prefix}section"] ) ) {
427 $section = $params[
"{$prefix}section"];
428 $newRev = MutableRevisionRecord::newFromParentRevision( $rev );
429 $content = $rev->getContent( SlotRecord::MAIN, RevisionRecord::FOR_THIS_USER,
433 [
'apierror-missingcontent-revid-role', $rev->getId(), SlotRecord::MAIN ],
'missingcontent'
439 [
"apierror-compare-nosuch{$prefix}section",
wfEscapeWikiText( $section ) ],
440 "nosuch{$prefix}section"
443 $newRev->setContent( SlotRecord::MAIN,
$content );
446 return [ $newRev, $rev, $rev ];
455 $newRev = MutableRevisionRecord::newFromParentRevision( $rev );
457 $newRev = $this->revisionStore->newMutableRevisionFromArray( [
461 foreach ( $params[
"{$prefix}slots"] as $role ) {
462 $text = $params[
"{$prefix}text-{$role}"];
463 if ( $text ===
null ) {
465 if ( $role === SlotRecord::MAIN ) {
466 $this->
dieWithError( [
'apierror-compare-maintextrequired', $prefix ] );
471 foreach ( [
'section',
'contentmodel',
'contentformat' ] as $param ) {
472 if ( isset( $params[
"{$prefix}{$param}-{$role}"] ) ) {
474 'apierror-compare-notext',
481 $newRev->removeSlot( $role );
485 $model = $params[
"{$prefix}contentmodel-{$role}"];
486 $format = $params[
"{$prefix}contentformat-{$role}"];
488 if ( !$model && $rev && $rev->hasSlot( $role ) ) {
489 $model = $rev->getSlot( $role, RevisionRecord::RAW )->getModel();
491 if ( !$model &&
$title && $role === SlotRecord::MAIN ) {
493 $model =
$title->getContentModel();
500 $this->
addWarning( [
'apiwarn-compare-nocontentmodel', $model ] );
507 'wrap' =>
ApiMessage::create(
'apierror-contentserializationexception',
'parseerror' )
511 if ( $params[
"{$prefix}pst"] ) {
519 $section = $params[
"{$prefix}section-{$role}"];
520 if ( $section !==
null && $section !==
'' ) {
522 $this->
dieWithError(
"apierror-compare-no{$prefix}revision" );
524 $oldContent = $rev->getContent( $role, RevisionRecord::FOR_THIS_USER, $this->
getUser() );
525 if ( !$oldContent ) {
527 [
'apierror-missingcontent-revid-role', $rev->getId(),
wfEscapeWikiText( $role ) ],
531 if ( !$oldContent->getContentHandler()->supportsSections() ) {
536 }
catch ( Exception $ex ) {
541 $this->
dieWithError( [
'apierror-sectionreplacefailed' ] );
546 if ( $role === SlotRecord::MAIN && isset( $params[
"{$prefix}section"] ) ) {
547 $section = $params[
"{$prefix}section"];
551 [
"apierror-compare-nosuch{$prefix}section",
wfEscapeWikiText( $section ) ],
552 "nosuch{$prefix}section"
557 $newRev->setContent( $role,
$content );
559 return [ $newRev, $rev, null ];
569 private function setVals( &$vals, $prefix, $rev ) {
572 if ( isset( $this->props[
'ids'] ) ) {
573 $vals[
"{$prefix}id"] =
$title->getArticleID();
574 $vals[
"{$prefix}revid"] = $rev->getId();
576 if ( isset( $this->props[
'title'] ) ) {
579 if ( isset( $this->props[
'size'] ) ) {
580 $vals[
"{$prefix}size"] = $rev->getSize();
584 if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
585 $vals[
"{$prefix}texthidden"] =
true;
589 if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
590 $vals[
"{$prefix}userhidden"] =
true;
593 if ( isset( $this->props[
'user'] ) ) {
594 $user = $rev->getUser( RevisionRecord::FOR_THIS_USER, $this->
getUser() );
596 $vals[
"{$prefix}user"] = $user->getName();
597 $vals[
"{$prefix}userid"] = $user->getId();
601 if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
602 $vals[
"{$prefix}commenthidden"] =
true;
605 if ( isset( $this->props[
'comment'] ) || isset( $this->props[
'parsedcomment'] ) ) {
606 $comment = $rev->getComment( RevisionRecord::FOR_THIS_USER, $this->
getUser() );
607 if ( $comment !==
null ) {
608 if ( isset( $this->props[
'comment'] ) ) {
609 $vals[
"{$prefix}comment"] = $comment->text;
618 $this->
getMain()->setCacheMode(
'private' );
619 if ( $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
620 $vals[
"{$prefix}suppressed"] =
true;
625 if ( !empty( $rev->isArchive ) ) {
626 $this->
getMain()->setCacheMode(
'private' );
627 $vals[
"{$prefix}archive"] =
true;
633 $slotRoles = $this->slotRoleRegistry->getKnownRoles();
634 sort( $slotRoles, SORT_STRING );
654 'section-{slot}' => [
658 'contentformat-{slot}' => [
662 'contentmodel-{slot}' => [
687 foreach ( $fromToParams as $k => $v ) {
693 foreach ( $fromToParams as $k => $v ) {
734 'action=compare&fromrev=1&torev=2'
735 =>
'apihelp-compare-example-1',