211 $refreshCache =
false,
$unhide =
false
229 wfDebug(
"DifferenceEngine old '$old' new '$new' rcid '$rcid'\n" );
231 $this->mOldid = $old;
232 $this->mNewid = $new;
233 $this->mRefreshCache = $refreshCache;
242 if ( $this->isSlotDiffRenderer ) {
243 throw new LogicException( __METHOD__ .
' called in slot diff renderer mode' );
246 if ( $this->slotDiffRenderers ===
null ) {
252 $this->slotDiffRenderers = array_map(
function ( $contents ) {
254 $content = $contents[
'new'] ?: $contents[
'old'];
268 $this->isSlotDiffRenderer =
true;
277 if ( $this->isContentOverridden ) {
279 SlotRecord::MAIN => [
288 $newSlots = $this->mNewRev->getRevisionRecord()->getSlots()->getSlots();
289 if ( $this->mOldRev ) {
290 $oldSlots = $this->mOldRev->getRevisionRecord()->getSlots()->getSlots();
298 $roles = array_merge( array_keys( $newSlots ), array_keys( $oldSlots ) );
301 foreach ( $roles as $role ) {
303 'old' => isset( $oldSlots[$role] ) ? $oldSlots[$role]->getContent() :
null,
304 'new' => isset( $newSlots[$role] ) ? $newSlots[$role]->getContent() :
null,
308 if ( isset( $slots[SlotRecord::MAIN] ) ) {
309 $slots = [ SlotRecord::MAIN => $slots[SlotRecord::MAIN] ] + $slots;
326 $this->mReducedLineNumbers = $value;
335 if ( $this->mDiffLang ===
null ) {
336 # Default language in which the diff text is written.
337 $this->mDiffLang = $this->
getTitle()->getPageLanguage();
382 return $this->mOldRev ? $this->mOldRev->getRevisionRecord() :
null;
391 return $this->mNewRev ? $this->mNewRev->getRevisionRecord() :
null;
403 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
404 if ( $permissionManager->userHasRight( $this->getUser(),
'deletedhistory' ) ) {
407 $row =
$dbr->selectRow(
409 array_merge( $arQuery[
'fields'], [
'ar_namespace',
'ar_title' ] ),
410 [
'ar_rev_id' => $id ],
420 'target' =>
$title->getPrefixedText(),
421 'timestamp' => $rev->getTimestamp()
439 return "[$link $id]";
449 if ( $this->mOldRev ===
null ||
450 ( $this->mOldRev && $this->mOldContent ===
null )
454 if ( $this->mNewRev ===
null ||
455 ( $this->mNewRev && $this->mNewContent ===
null )
460 $out->setPageTitle( $this->
msg(
'errorpagetitle' ) );
461 $msg = $this->
msg(
'difference-missing-revision' )
462 ->params( $this->
getLanguage()->listToText( $missing ) )
463 ->numParams( count( $missing ) )
465 $out->addHTML( $msg );
469 # Allow frames except in certain special cases
471 $out->allowClickjacking();
472 $out->setRobotPolicy(
'noindex,nofollow' );
475 Hooks::run(
'DifferenceEngineShowDiffPage', [ $out ] );
478 if (
Hooks::run(
'DifferenceEngineShowDiffPageMaybeShowMissingRevision', [ $this ] ) ) {
486 if ( $this->mNewPage ) {
487 $permErrors = $this->mNewPage->getUserPermissionsErrors(
'read', $user );
489 if ( $this->mOldPage ) {
491 $this->mOldPage->getUserPermissionsErrors(
'read', $user ) );
493 if ( count( $permErrors ) ) {
500 # Carry over 'diffonly' param via navigation links
501 if ( $diffOnly != $user->getBoolOption(
'diffonly' ) ) {
502 $query[
'diffonly'] = $diffOnly;
504 # Cascade unhide param in links for easy deletion browsing
505 if ( $this->unhide ) {
506 $query[
'unhide'] = 1;
509 # Check if one of the revisions is deleted/suppressed
510 $deleted = $suppressed =
false;
511 $allowed = $this->mNewRev->userCan( RevisionRecord::DELETED_TEXT, $user );
515 # mOldRev is false if the difference engine is called with a "vague" query for
516 # a diff between a version V and its previous version V' AND the version V
517 # is the first version of that article. In that case, V' does not exist.
518 if ( $this->mOldRev ===
false ) {
519 if ( $this->mNewPage ) {
520 $out->setPageTitle( $this->
msg(
'difference-title', $this->mNewPage->getPrefixedText() ) );
525 Hooks::run(
'DifferenceEngineOldHeaderNoOldRev', [ &$oldHeader ] );
527 Hooks::run(
'DiffViewHeader', [ $this, $this->mOldRev, $this->mNewRev ] );
529 if ( !$this->mOldPage || !$this->mNewPage ) {
532 } elseif ( $this->mNewPage->equals( $this->mOldPage ) ) {
533 $out->setPageTitle( $this->
msg(
'difference-title', $this->mNewPage->getPrefixedText() ) );
536 $out->setPageTitle( $this->
msg(
'difference-title-multipage',
537 $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText() ) );
538 $out->addSubtitle( $this->
msg(
'difference-multipage' ) );
542 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
544 if ( $samePage && $this->mNewPage && $permissionManager->quickUserCan(
545 'edit', $user, $this->mNewPage
547 if ( $this->mNewRev->isCurrent() && $permissionManager->quickUserCan(
548 'rollback', $user, $this->mNewPage
552 if ( $rollbackLink ) {
553 $out->preventClickjacking();
554 $rollback =
"\u{00A0}\u{00A0}\u{00A0}" . $rollbackLink;
561 $undoLink = Html::element(
'a', [
562 'href' => $this->mNewPage->getLocalURL( [
564 'undoafter' => $this->mOldid,
565 'undo' => $this->mNewid
569 $this->
msg(
'editundo' )->text()
571 $revisionTools[
'mw-diff-undo'] = $undoLink;
575 # Make "previous revision link"
576 if ( $samePage && $this->mOldPage && $this->mOldRev->getPrevious() ) {
579 $this->
msg(
'previousdiff' )->escaped(),
580 [
'id' =>
'differences-prevlink' ],
581 [
'diff' =>
'prev',
'oldid' => $this->mOldid ] + $query
584 $prevlink =
"\u{00A0}";
587 if ( $this->mOldRev->isMinor() ) {
597 $oldHeader =
'<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader .
'</strong></div>' .
598 '<div id="mw-diff-otitle2">' .
600 '<div id="mw-diff-otitle3">' . $oldminor .
602 '<div id="mw-diff-otitle5">' . $oldChangeTags[0] .
'</div>' .
603 '<div id="mw-diff-otitle4">' . $prevlink .
'</div>';
606 Hooks::run(
'DifferenceEngineOldHeader', [ $this, &$oldHeader, $prevlink, $oldminor,
607 $diffOnly, $ldel, $this->unhide ] );
609 if ( $this->mOldRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
611 if ( $this->mOldRev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
616 # Check if this user can see the revisions
617 if ( !$this->mOldRev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
622 $out->addJsConfigVars( [
623 'wgDiffOldId' => $this->mOldid,
624 'wgDiffNewId' => $this->mNewid,
627 # Make "next revision link"
628 # Skip next link on the top revision
629 if ( $samePage && $this->mNewPage && !$this->mNewRev->isCurrent() ) {
632 $this->
msg(
'nextdiff' )->escaped(),
633 [
'id' =>
'differences-nextlink' ],
634 [
'diff' =>
'next',
'oldid' => $this->mNewid ] + $query
637 $nextlink =
"\u{00A0}";
640 if ( $this->mNewRev->isMinor() ) {
646 # Handle RevisionDelete links...
649 # Allow extensions to define their own revision tools
651 [ $this->mNewRev, &$revisionTools, $this->mOldRev, $user ] );
652 $formattedRevisionTools = [];
654 foreach ( $revisionTools as $key => $tool ) {
655 $toolClass = is_string( $key ) ? $key :
'mw-diff-tool';
656 $element = Html::rawElement(
658 [
'class' => $toolClass ],
659 $this->
msg(
'parentheses' )->rawParams( $tool )->escaped()
661 $formattedRevisionTools[] = $element;
664 ' ' . implode(
' ', $formattedRevisionTools );
667 $newHeader =
'<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader .
'</strong></div>' .
670 '<div id="mw-diff-ntitle3">' . $newminor .
672 '<div id="mw-diff-ntitle5">' . $newChangeTags[0] .
'</div>' .
673 '<div id="mw-diff-ntitle4">' . $nextlink . $this->
markPatrolledLink() .
'</div>';
676 Hooks::run(
'DifferenceEngineNewHeader', [ $this, &$newHeader, $formattedRevisionTools,
677 $nextlink, $rollback, $newminor, $diffOnly, $rdel, $this->unhide ] );
679 if ( $this->mNewRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
681 if ( $this->mNewRev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
686 # If the diff cannot be shown due to a deleted revision, then output
687 # the diff header and links to unhide (if available)...
688 if ( $deleted && ( !$this->unhide || !$allowed ) ) {
691 $out->addHTML( $this->
addHeader(
'', $oldHeader, $newHeader, $multi ) );
693 $msg = $suppressed ?
'rev-suppressed-no-diff' :
'rev-deleted-no-diff';
694 # Give explanation for why revision is not visible
695 $out->wrapWikiMsg(
"<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
698 # Give explanation and add a link to view the diff...
699 $query = $this->
getRequest()->appendQueryValue(
'unhide',
'1' );
700 $link = $this->
getTitle()->getFullURL( $query );
701 $msg = $suppressed ?
'rev-suppressed-unhide-diff' :
'rev-deleted-unhide-diff';
703 "<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
707 # Otherwise, output a regular diff...
709 # Add deletion notice if the user is viewing deleted content
712 $msg = $suppressed ?
'rev-suppressed-diff-view' :
'rev-deleted-diff-view';
713 $notice =
"<div id='mw-$msg' class='mw-warning plainlinks'>\n" .
714 $this->
msg( $msg )->parse() .
717 $this->
showDiff( $oldHeader, $newHeader, $notice );
734 if ( $this->mMarkPatrolledLink ===
null ) {
737 if ( !$linkInfo || !$this->mNewPage ) {
738 $this->mMarkPatrolledLink =
'';
740 $this->mMarkPatrolledLink =
' <span class="patrollink" data-mw="interface">[' .
743 $this->
msg(
'markaspatrolleddiff' )->escaped(),
746 'action' =>
'markpatrolled',
747 'rcid' => $linkInfo[
'rcid'],
751 Hooks::run(
'DifferenceEngineMarkPatrolledLink', [ $this,
752 &$this->mMarkPatrolledLink, $linkInfo[
'rcid'] ] );
768 $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
773 $config->get(
'UseRCPatrol' ) &&
775 $permissionManager->quickUserCan(
'patrol', $user, $this->mNewPage ) &&
784 'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
785 'rc_this_oldid' => $this->mNewid,
791 if ( $change && !$change->getPerformer()->equals( $user ) ) {
792 $rcid = $change->getAttribute(
'rc_id' );
803 Hooks::run(
'DifferenceEngineMarkPatrolledRCID', [ &$rcid, $this, $change, $user ] );
807 $this->
getOutput()->preventClickjacking();
808 if ( $permissionManager->userHasRight( $user,
'writeapi' ) ) {
809 $this->
getOutput()->addModules(
'mediawiki.page.patrol.ajax' );
829 if ( $link !==
'' ) {
830 $link =
"\u{00A0}\u{00A0}\u{00A0}" . $link .
' ';
842 if ( $this->isContentOverridden ) {
846 throw new LogicException(
848 .
' is not supported after calling setContent(). Use setRevisions() instead.'
854 # Add "current version as of X" title
855 $out->addHTML(
"<hr class='diff-hr' id='mw-oldid' />
856 <h2 class='diff-currentversion-title'>{$revHeader}</h2>\n" );
857 # Page content may be handled by a hooked call instead...
858 if (
Hooks::run(
'ArticleContentOnDiff', [ $this, $out ] ) ) {
860 if ( !$this->mNewPage ) {
867 $out->setRevisionId( $this->mNewid );
868 $out->setRevisionTimestamp( $this->mNewRev->getTimestamp() );
869 $out->setArticleFlag(
true );
871 if ( !
Hooks::run(
'ArticleRevisionViewCustom',
876 } elseif ( !
Hooks::run(
'ArticleContentViewCustom',
877 [ $this->mNewContent, $this->mNewPage, $out ],
'1.32' )
883 if ( $this->
getTitle()->equals( $this->mNewPage ) ) {
895 # WikiPage::getParserOutput() should not return false, but just in case
896 if ( $parserOutput ) {
898 if (
Hooks::run(
'DifferenceEngineRenderRevisionAddParserOutput',
899 [ $this, $out, $parserOutput, $wikiPage ] )
901 $out->addParserOutput( $parserOutput, [
902 'enableSectionEditLinks' => $this->mNewRev->isCurrent()
903 && MediaWikiServices::getInstance()->getPermissionManager()->quickUserCan(
906 $this->mNewRev->getTitle()
915 if (
Hooks::run(
'DifferenceEngineRenderRevisionShowFinalPatrolLink' ) ) {
916 # Add redundant patrol link on bottom...
928 if ( !$rev->
getId() ) {
938 return $parserOutput;
951 public function showDiff( $otitle, $ntitle, $notice =
'' ) {
953 Hooks::run(
'DifferenceEngineShowDiff', [ $this ] );
955 $diff = $this->
getDiff( $otitle, $ntitle, $notice );
956 if ( $diff ===
false ) {
972 if ( !$this->isSlotDiffRenderer ) {
974 'mediawiki.interface.helpers.styles',
975 'mediawiki.diff.styles'
978 $slotDiffRenderer->addModules( $this->
getOutput() );
992 public function getDiff( $otitle, $ntitle, $notice =
'' ) {
994 if ( $body ===
false ) {
1000 if ( $body ===
'' ) {
1001 $notice .=
'<div class="mw-diff-empty">' .
1002 $this->
msg(
'diff-empty' )->parse() .
1006 return $this->
addHeader( $body, $otitle, $ntitle, $multi, $notice );
1015 $this->mCacheHit =
true;
1017 if ( !$this->isContentOverridden ) {
1020 } elseif ( $this->mOldRev &&
1021 !$this->mOldRev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
1024 } elseif ( $this->mNewRev &&
1025 !$this->mNewRev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
1030 if ( $this->mOldRev ===
false || ( $this->mOldRev && $this->mNewRev &&
1031 $this->mOldRev->getId() && $this->mOldRev->getId() == $this->mNewRev->getId() )
1033 if (
Hooks::run(
'DifferenceEngineShowEmptyOldContent', [ $this ] ) ) {
1041 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1042 if ( $this->mOldid && $this->mNewid ) {
1046 if ( $key ===
null ) {
1051 if ( !$this->mRefreshCache ) {
1052 $difftext =
$cache->get( $key );
1053 if ( is_string( $difftext ) ) {
1056 $difftext .=
"\n<!-- diff cache key $key -->\n";
1062 $this->mCacheHit =
false;
1074 $slotDiff = $slotDiffRenderer->getDiff( $slotContents[$role][
'old'],
1075 $slotContents[$role][
'new'] );
1076 if ( $slotDiff && $role !== SlotRecord::MAIN ) {
1081 $difftext .= $slotDiff;
1085 $diffEngine = $this;
1088 if ( !
Hooks::run(
'AbortDiffCache', [ &$diffEngine ] ) ) {
1090 } elseif ( $key !==
false && $difftext !==
false ) {
1092 $cache->set( $key, $difftext, 7 * 86400 );
1097 if ( $difftext !==
false ) {
1112 if ( !isset( $diffRenderers[$role] ) ) {
1117 $slotDiff = $diffRenderers[$role]->getDiff( $slotContents[$role][
'old'],
1118 $slotContents[$role][
'new'] );
1123 if ( $role !== SlotRecord::MAIN ) {
1141 $columnCount = $this->mOldRev ? 4 : 2;
1143 return Html::rawElement(
'tr', [
'class' =>
'mw-diff-slot-header',
'lang' => $userLang ],
1144 Html::element(
'th', [
'colspan' => $columnCount ], $headerText ) );
1174 if ( !$this->mOldid || !$this->mNewid ) {
1175 throw new MWException(
'mOldid and mNewid must be set to get diff cache key.' );
1181 $engine ===
'php' ? false : $engine,
1183 "old-{$this->mOldid}",
1184 "rev-{$this->mNewid}"
1187 if ( $engine ===
'wikidiff2' ) {
1188 $params[] = phpversion(
'wikidiff2' );
1191 if ( !$this->isSlotDiffRenderer ) {
1193 $params = array_merge( $params, $slotDiffRenderer->getExtraCacheKeys() );
1211 $this->mOldid = 123456789;
1212 $this->mNewid = 987654321;
1216 if ( $cacheString ) {
1217 return [ $cacheString ];
1230 if ( array_slice( $params, 0, count( $standardParams ) ) === $standardParams ) {
1231 $params = array_slice( $params, count( $standardParams ) );
1254 && $this->isSlotDiffRenderer
1260 throw new Exception( get_class( $this ) .
': could not maintain backwards compatibility. '
1261 .
'Please use a SlotDiffRenderer.' );
1263 return $slotDiffRenderer->getDiff( $old, $new ) . $this->
getDebugString();
1280 ->getSlotDiffRenderer( $this->
getContext() );
1284 throw new Exception(
'The slot diff renderer for text content should be a '
1285 .
'TextSlotDiffRenderer subclass' );
1287 return $slotDiffRenderer->getTextDiff( $otext, $ntext ) . $this->
getDebugString();
1297 $diffEngine = MediaWikiServices::getInstance()->getMainConfig()
1298 ->get(
'DiffEngine' );
1299 $externalDiffEngine = MediaWikiServices::getInstance()->getMainConfig()
1300 ->get(
'ExternalDiffEngine' );
1302 if ( $diffEngine ===
null ) {
1303 $engines = [
'external',
'wikidiff2',
'php' ];
1305 $engines = [ $diffEngine ];
1308 $failureReason =
null;
1309 foreach ( $engines as $engine ) {
1310 switch ( $engine ) {
1312 if ( is_string( $externalDiffEngine ) ) {
1313 if ( is_executable( $externalDiffEngine ) ) {
1314 return $externalDiffEngine;
1316 $failureReason =
'ExternalDiffEngine config points to a non-executable';
1317 if ( $diffEngine ===
null ) {
1318 wfDebug(
"$failureReason, ignoring" );
1321 $failureReason =
'ExternalDiffEngine config is set to a non-string value';
1322 if ( $diffEngine ===
null && $externalDiffEngine ) {
1323 wfWarn(
"$failureReason, ignoring" );
1329 if ( function_exists(
'wikidiff2_do_diff' ) ) {
1332 $failureReason =
'wikidiff2 is not available';
1340 throw new DomainException(
'Invalid value for $wgDiffEngine: ' . $engine );
1343 throw new UnexpectedValueException(
"Cannot use diff engine '$engine': $failureReason" );
1360 ->getSlotDiffRenderer( $this->
getContext() );
1364 throw new Exception(
'The slot diff renderer for text content should be a '
1365 .
'TextSlotDiffRenderer subclass' );
1367 return $slotDiffRenderer->getTextDiff( $otext, $ntext ) . $this->
getDebugString();
1379 if ( !$this->enableDebugComment ) {
1383 if ( $this->
getConfig()->
get(
'ShowHostnames' ) ) {
1388 return "<!-- diff generator: " .
1389 implode(
" ", array_map(
"htmlspecialchars", $data ) ) .
1395 if ( $engine ===
'wikidiff2' ) {
1396 return $this->
debug(
'wikidiff2' );
1397 } elseif ( $engine ===
'php' ) {
1398 return $this->
debug(
'native PHP' );
1400 return $this->
debug(
"external $engine" );
1412 if ( $this->
getEngine() ===
'wikidiff2' &&
1413 version_compare( phpversion(
'wikidiff2' ),
'1.5.1',
'>=' )
1428 return preg_replace_callback(
1429 '/<!--LINE (\d+)-->/',
1430 [ $this,
'localiseLineNumbersCb' ],
1436 if (
$matches[1] ===
'1' && $this->mReducedLineNumbers ) {
1440 return $this->
msg(
'lineno' )->numParams(
$matches[1] )->escaped();
1450 return preg_replace_callback(
1451 '/class="mw-diff-movedpara-(left|right)"/',
1452 [ $this,
'addLocalisedTitleTooltipsCb' ],
1463 'diff-paragraph-moved-toold' :
1464 'diff-paragraph-moved-tonew';
1465 return $matches[0] .
' title="' . $this->
msg( $key )->escaped() .
'"';
1476 !$this->mOldRev || !$this->mNewRev
1477 || !$this->mOldPage || !$this->mNewPage
1478 || !$this->mOldPage->equals( $this->mNewPage )
1483 if ( $this->mOldRev->getTimestamp() > $this->mNewRev->getTimestamp() ) {
1493 $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev, $newRev, 1000 );
1494 if ( $nEdits > 0 && $nEdits <= 1000 ) {
1496 $users = $this->mNewPage->getAuthorsBetween( $oldRev, $newRev, $limit );
1497 $numUsers = count( $users );
1499 if ( $numUsers == 1 && $users[0] == $newRev->getUserText( RevisionRecord::RAW ) ) {
1519 if ( $numUsers === 0 ) {
1520 $msg =
'diff-multi-sameuser';
1521 } elseif ( $numUsers > $limit ) {
1522 $msg =
'diff-multi-manyusers';
1525 $msg =
'diff-multi-otherusers';
1528 return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse();
1538 if ( !$rev->
userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
1558 $timestamp =
$lang->userTimeAndDate( $revtimestamp, $user );
1559 $dateofrev =
$lang->userDate( $revtimestamp, $user );
1560 $timeofrev =
$lang->userTime( $revtimestamp, $user );
1563 $rev->
isCurrent() ?
'currentrev-asof' :
'revisionasof',
1569 if ( $complete !==
'complete' ) {
1576 [
'oldid' => $rev->
getId() ] );
1579 $editQuery = [
'action' =>
'edit' ];
1581 $editQuery[
'oldid'] = $rev->
getId();
1584 $key = MediaWikiServices::getInstance()->getPermissionManager()
1585 ->quickUserCan(
'edit', $user,
$title ) ?
'editold' :
'viewsourceold';
1586 $msg = $this->
msg( $key )->escaped();
1587 $editLink = $this->
msg(
'parentheses' )->rawParams(
1589 $header .=
' ' . Html::rawElement(
1591 [
'class' =>
'mw-diff-edit' ],
1594 if ( $rev->
isDeleted( RevisionRecord::DELETED_TEXT ) ) {
1597 [
'class' =>
'history-deleted' ],
1602 $header = Html::rawElement(
'span', [
'class' =>
'history-deleted' ],
$header );
1620 public function addHeader( $diff, $otitle, $ntitle, $multi =
'', $notice =
'' ) {
1623 $header = Html::openElement(
'table', [
1624 'class' => [
'diff',
'diff-contentalign-' . $this->
getDiffLang()->alignStart() ],
1625 'data-mw' =>
'interface',
1627 $userLang = htmlspecialchars( $this->
getLanguage()->getHtmlCode() );
1629 if ( !$diff && !$otitle ) {
1631 <tr class=\"diff-title\" lang=\"{$userLang}\">
1632 <td class=\"diff-ntitle\">{$ntitle}</td>
1638 <col class=\"diff-marker\" />
1639 <col class=\"diff-content\" />
1640 <col class=\"diff-marker\" />
1641 <col class=\"diff-content\" />";
1648 if ( $otitle || $ntitle ) {
1650 <tr class=\"diff-title\" lang=\"{$userLang}\">
1651 <td colspan=\"$colspan\" class=\"diff-otitle\">{$otitle}</td>
1652 <td colspan=\"$colspan\" class=\"diff-ntitle\">{$ntitle}</td>
1657 if ( $multi !=
'' ) {
1658 $header .=
"<tr><td colspan=\"{$multiColspan}\" " .
1659 "class=\"diff-multi\" lang=\"{$userLang}\">{$multi}</td></tr>";
1661 if ( $notice !=
'' ) {
1662 $header .=
"<tr><td colspan=\"{$multiColspan}\" " .
1663 "class=\"diff-notice\" lang=\"{$userLang}\">{$notice}</td></tr>";
1666 return $header . $diff .
"</table>";
1677 $this->mOldContent = $oldContent;
1678 $this->mNewContent = $newContent;
1680 $this->mTextLoaded = 2;
1681 $this->mRevisionsLoaded =
true;
1682 $this->isContentOverridden =
true;
1683 $this->slotDiffRenderers =
null;
1694 if ( $oldRevision ) {
1695 $this->mOldRev =
new Revision( $oldRevision );
1696 $this->mOldid = $oldRevision->getId();
1700 $this->mOldContent = $oldRevision->getContent( SlotRecord::MAIN,
1701 RevisionRecord::FOR_THIS_USER, $this->
getUser() );
1703 $this->mOldPage =
null;
1704 $this->mOldRev = $this->mOldid =
false;
1706 $this->mNewRev =
new Revision( $newRevision );
1707 $this->mNewid = $newRevision->
getId();
1709 $this->mNewContent = $newRevision->
getContent( SlotRecord::MAIN,
1710 RevisionRecord::FOR_THIS_USER, $this->
getUser() );
1712 $this->mRevisionsIdsLoaded = $this->mRevisionsLoaded =
true;
1713 $this->mTextLoaded = $oldRevision ? 2 : 1;
1714 $this->isContentOverridden =
false;
1715 $this->slotDiffRenderers =
null;
1725 $this->mDiffLang =
$lang;
1740 $rl = MediaWikiServices::getInstance()->getRevisionLookup();
1741 if ( $new ===
'prev' ) {
1743 $newid = intval( $old );
1745 $newRev = $rl->getRevisionById( $newid );
1747 $oldRev = $rl->getPreviousRevision( $newRev );
1749 $oldid = $oldRev->getId();
1752 } elseif ( $new ===
'next' ) {
1754 $oldid = intval( $old );
1756 $oldRev = $rl->getRevisionById( $oldid );
1758 $newRev = $rl->getNextRevision( $oldRev );
1760 $newid = $newRev->getId();
1764 $oldid = intval( $old );
1765 $newid = intval( $new );
1768 return [ $oldid, $newid ];
1775 if ( $this->mRevisionsIdsLoaded ) {
1779 $this->mRevisionsIdsLoaded =
true;
1785 if ( $new ===
'next' && $this->mNewid ===
false ) {
1786 # if no result, NewId points to the newest old revision. The only newer
1787 # revision is cur, which is "0".
1792 'NewDifferenceEngine',
1793 [ $this->
getTitle(), &$this->mOldid, &$this->mNewid, $old, $new ]
1811 if ( $this->mRevisionsLoaded ) {
1812 return $this->isContentOverridden || ( $this->mOldRev !==
null && $this->mNewRev !== null );
1816 $this->mRevisionsLoaded =
true;
1821 if ( $this->mNewid ) {
1827 Revision::READ_NORMAL
1831 if ( !$this->mNewRev instanceof
Revision ) {
1836 $this->mNewid = $this->mNewRev->getId();
1837 if ( $this->mNewid ) {
1838 $this->mNewPage = $this->mNewRev->getTitle();
1840 $this->mNewPage =
null;
1844 $this->mOldRev =
false;
1845 if ( $this->mOldid ) {
1847 } elseif ( $this->mOldid === 0 ) {
1848 $rev = $this->mNewRev->getPrevious();
1850 $this->mOldid = $rev->getId();
1851 $this->mOldRev = $rev;
1854 $this->mOldid =
false;
1855 $this->mOldRev =
false;
1859 if ( is_null( $this->mOldRev ) ) {
1863 if ( $this->mOldRev && $this->mOldRev->getId() ) {
1864 $this->mOldPage = $this->mOldRev->getTitle();
1866 $this->mOldPage =
null;
1871 $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
1872 if ( $this->mOldid !==
false ) {
1873 $tagIds =
$dbr->selectFieldValues(
1876 [
'ct_rev_id' => $this->mOldid ],
1880 foreach ( $tagIds as $tagId ) {
1882 $tags[] = $changeTagDefStore->getName( (
int)$tagId );
1887 $this->mOldTags = implode(
',', $tags );
1889 $this->mOldTags =
false;
1892 $tagIds =
$dbr->selectFieldValues(
1895 [
'ct_rev_id' => $this->mNewid ],
1899 foreach ( $tagIds as $tagId ) {
1901 $tags[] = $changeTagDefStore->getName( (
int)$tagId );
1906 $this->mNewTags = implode(
',', $tags );
1920 if ( $this->mTextLoaded == 2 ) {
1922 && $this->mNewContent;
1926 $this->mTextLoaded = 2;
1932 if ( $this->mOldRev ) {
1933 $this->mOldContent = $this->mOldRev->getContent(
1934 RevisionRecord::FOR_THIS_USER, $this->
getUser()
1936 if ( $this->mOldContent ===
null ) {
1941 $this->mNewContent = $this->mNewRev->getContent(
1942 RevisionRecord::FOR_THIS_USER, $this->
getUser()
1944 Hooks::run(
'DifferenceEngineLoadTextAfterNewContentIsLoaded', [ $this ] );
1945 if ( $this->mNewContent ===
null ) {
1958 if ( $this->mTextLoaded >= 1 ) {
1962 $this->mTextLoaded = 1;
1968 $this->mNewContent = $this->mNewRev->getContent(
1969 RevisionRecord::FOR_THIS_USER, $this->
getUser()
1972 Hooks::run(
'DifferenceEngineAfterLoadNewText', [ $this ] );