Go to the documentation of this file.
229 wfDebug(
"DifferenceEngine old '$old' new '$new' rcid '$rcid'\n" );
231 $this->mOldid = $old;
232 $this->mNewid = $new;
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 if ( $this->
getUser()->isAllowed(
'deletedhistory' ) ) {
406 $row =
$dbr->selectRow(
408 array_merge( $arQuery[
'fields'], [
'ar_namespace',
'ar_title' ] ),
409 [
'ar_rev_id' => $id ],
419 'target' =>
$title->getPrefixedText(),
420 'timestamp' =>
$rev->getTimestamp()
438 return "[$link $id]";
448 if ( $this->mOldRev ===
null ||
449 ( $this->mOldRev && $this->mOldContent ===
null )
453 if ( $this->mNewRev ===
null ||
454 ( $this->mNewRev && $this->mNewContent ===
null )
459 $out->setPageTitle( $this->
msg(
'errorpagetitle' ) );
460 $msg = $this->
msg(
'difference-missing-revision' )
461 ->params( $this->
getLanguage()->listToText( $missing ) )
462 ->numParams(
count( $missing ) )
464 $out->addHTML( $msg );
468 # Allow frames except in certain special cases
470 $out->allowClickjacking();
471 $out->setRobotPolicy(
'noindex,nofollow' );
477 if (
Hooks::run(
'DifferenceEngineShowDiffPageMaybeShowMissingRevision', [ $this ] ) ) {
485 if ( $this->mNewPage ) {
486 $permErrors = $this->mNewPage->getUserPermissionsErrors(
'read',
$user );
488 if ( $this->mOldPage ) {
490 $this->mOldPage->getUserPermissionsErrors(
'read',
$user ) );
492 if (
count( $permErrors ) ) {
499 # Carry over 'diffonly' param via navigation links
500 if ( $diffOnly !=
$user->getBoolOption(
'diffonly' ) ) {
501 $query[
'diffonly'] = $diffOnly;
503 # Cascade unhide param in links for easy deletion browsing
504 if ( $this->unhide ) {
508 # Check if one of the revisions is deleted/suppressed
514 # mOldRev is false if the difference engine is called with a "vague" query for
515 # a diff between a version V and its previous version V' AND the version V
516 # is the first version of that article. In that case, V' does not exist.
517 if ( $this->mOldRev ===
false ) {
518 if ( $this->mNewPage ) {
519 $out->setPageTitle( $this->
msg(
'difference-title', $this->mNewPage->getPrefixedText() ) );
524 Hooks::run(
'DifferenceEngineOldHeaderNoOldRev', [ &$oldHeader ] );
526 Hooks::run(
'DiffViewHeader', [ $this, $this->mOldRev, $this->mNewRev ] );
528 if ( !$this->mOldPage || !$this->mNewPage ) {
531 } elseif ( $this->mNewPage->equals( $this->mOldPage ) ) {
532 $out->setPageTitle( $this->
msg(
'difference-title', $this->mNewPage->getPrefixedText() ) );
535 $out->setPageTitle( $this->
msg(
'difference-title-multipage',
536 $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText() ) );
537 $out->addSubtitle( $this->
msg(
'difference-multipage' ) );
541 if ( $samePage && $this->mNewPage && $this->mNewPage->quickUserCan(
'edit',
$user ) ) {
542 if ( $this->mNewRev->isCurrent() && $this->mNewPage->userCan(
'rollback',
$user ) ) {
544 if ( $rollbackLink ) {
545 $out->preventClickjacking();
546 $rollback =
"\u{00A0}\u{00A0}\u{00A0}" . $rollbackLink;
553 $undoLink = Html::element(
'a', [
554 'href' => $this->mNewPage->getLocalURL( [
556 'undoafter' => $this->mOldid,
557 'undo' => $this->mNewid
561 $this->
msg(
'editundo' )->text()
563 $revisionTools[
'mw-diff-undo'] = $undoLink;
567 # Make "previous revision link"
568 if ( $samePage && $this->mOldPage && $this->mOldRev->getPrevious() ) {
571 $this->
msg(
'previousdiff' )->escaped(),
572 [
'id' =>
'differences-prevlink' ],
573 [
'diff' =>
'prev',
'oldid' => $this->mOldid ] +
$query
576 $prevlink =
"\u{00A0}";
579 if ( $this->mOldRev->isMinor() ) {
589 $oldHeader =
'<div id="mw-diff-otitle1"><strong>' . $oldRevisionHeader .
'</strong></div>' .
590 '<div id="mw-diff-otitle2">' .
592 '<div id="mw-diff-otitle3">' .
$oldminor .
594 '<div id="mw-diff-otitle5">' . $oldChangeTags[0] .
'</div>' .
595 '<div id="mw-diff-otitle4">' . $prevlink .
'</div>';
599 $diffOnly, $ldel, $this->unhide ] );
608 # Check if this user can see the revisions
614 $out->addJsConfigVars( [
615 'wgDiffOldId' => $this->mOldid,
616 'wgDiffNewId' => $this->mNewid,
619 # Make "next revision link"
620 # Skip next link on the top revision
621 if ( $samePage && $this->mNewPage && !$this->mNewRev->isCurrent() ) {
624 $this->
msg(
'nextdiff' )->escaped(),
625 [
'id' =>
'differences-nextlink' ],
626 [
'diff' =>
'next',
'oldid' => $this->mNewid ] +
$query
629 $nextlink =
"\u{00A0}";
632 if ( $this->mNewRev->isMinor() ) {
638 # Handle RevisionDelete links...
641 # Allow extensions to define their own revision tools
643 [ $this->mNewRev, &$revisionTools, $this->mOldRev,
$user ] );
644 $formattedRevisionTools = [];
646 foreach ( $revisionTools
as $key => $tool ) {
647 $toolClass = is_string( $key ) ? $key :
'mw-diff-tool';
648 $element = Html::rawElement(
650 [
'class' => $toolClass ],
651 $this->
msg(
'parentheses' )->rawParams( $tool )->escaped()
653 $formattedRevisionTools[] = $element;
656 ' ' . implode(
' ', $formattedRevisionTools );
659 $newHeader =
'<div id="mw-diff-ntitle1"><strong>' . $newRevisionHeader .
'</strong></div>' .
662 '<div id="mw-diff-ntitle3">' .
$newminor .
664 '<div id="mw-diff-ntitle5">' . $newChangeTags[0] .
'</div>' .
665 '<div id="mw-diff-ntitle4">' . $nextlink . $this->
markPatrolledLink() .
'</div>';
668 Hooks::run(
'DifferenceEngineNewHeader', [ $this, &$newHeader, $formattedRevisionTools,
678 # If the diff cannot be shown due to a deleted revision, then output
679 # the diff header and links to unhide (if available)...
680 if ( $deleted && ( !$this->unhide || !$allowed ) ) {
683 $out->addHTML( $this->
addHeader(
'', $oldHeader, $newHeader, $multi ) );
685 $msg =
$suppressed ?
'rev-suppressed-no-diff' :
'rev-deleted-no-diff';
686 # Give explanation for why revision is not visible
687 $out->wrapWikiMsg(
"<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
690 # Give explanation and add a link to view the diff...
693 $msg =
$suppressed ?
'rev-suppressed-unhide-diff' :
'rev-deleted-unhide-diff';
695 "<div id='mw-$msg' class='mw-warning plainlinks'>\n$1\n</div>\n",
699 # Otherwise, output a regular diff...
701 # Add deletion notice if the user is viewing deleted content
704 $msg =
$suppressed ?
'rev-suppressed-diff-view' :
'rev-deleted-diff-view';
705 $notice =
"<div id='mw-$msg' class='mw-warning plainlinks'>\n" .
706 $this->
msg( $msg )->parse() .
709 $this->
showDiff( $oldHeader, $newHeader, $notice );
726 if ( $this->mMarkPatrolledLink ===
null ) {
729 if ( !$linkInfo || !$this->mNewPage ) {
730 $this->mMarkPatrolledLink =
'';
732 $this->mMarkPatrolledLink =
' <span class="patrollink" data-mw="interface">[' .
735 $this->
msg(
'markaspatrolleddiff' )->escaped(),
738 'action' =>
'markpatrolled',
739 'rcid' => $linkInfo[
'rcid'],
743 Hooks::run(
'DifferenceEngineMarkPatrolledLink', [ $this,
744 &$this->mMarkPatrolledLink, $linkInfo[
'rcid'] ] );
764 $config->get(
'UseRCPatrol' ) &&
765 $this->mNewPage && $this->mNewPage->quickUserCan(
'patrol',
$user ) &&
774 'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
775 'rc_this_oldid' => $this->mNewid,
781 if ( $change && !$change->getPerformer()->equals(
$user ) ) {
782 $rcid = $change->getAttribute(
'rc_id' );
793 Hooks::run(
'DifferenceEngineMarkPatrolledRCID', [ &$rcid, $this, $change,
$user ] );
797 $this->
getOutput()->preventClickjacking();
798 if (
$user->isAllowed(
'writeapi' ) ) {
799 $this->
getOutput()->addModules(
'mediawiki.page.patrol.ajax' );
819 if (
$link !==
'' ) {
820 $link =
"\u{00A0}\u{00A0}\u{00A0}" .
$link .
' ';
832 if ( $this->isContentOverridden ) {
836 throw new LogicException(
838 .
' is not supported after calling setContent(). Use setRevisions() instead.'
844 # Add "current version as of X" title
845 $out->addHTML(
"<hr class='diff-hr' id='mw-oldid' />
846 <h2 class='diff-currentversion-title'>{$revHeader}</h2>\n" );
847 # Page content may be handled by a hooked call instead...
850 if ( !$this->mNewPage ) {
857 $out->setRevisionId( $this->mNewid );
858 $out->setRevisionTimestamp( $this->mNewRev->getTimestamp() );
859 $out->setArticleFlag(
true );
861 if ( !
Hooks::run(
'ArticleRevisionViewCustom',
866 } elseif ( !
Hooks::run(
'ArticleContentViewCustom',
867 [ $this->mNewContent, $this->mNewPage,
$out ],
'1.32' )
873 if ( $this->
getTitle()->equals( $this->mNewPage ) ) {
885 # WikiPage::getParserOutput() should not return false, but just in case
886 if ( $parserOutput ) {
888 if (
Hooks::run(
'DifferenceEngineRenderRevisionAddParserOutput',
889 [ $this,
$out, $parserOutput, $wikiPage ] )
891 $out->addParserOutput( $parserOutput, [
892 'enableSectionEditLinks' => $this->mNewRev->isCurrent()
893 && $this->mNewRev->getTitle()->quickUserCan(
'edit', $this->
getUser() ),
901 if (
Hooks::run(
'DifferenceEngineRenderRevisionShowFinalPatrolLink' ) ) {
902 # Add redundant patrol link on bottom...
914 if ( !
$rev->getId() ) {
924 return $parserOutput;
937 public function showDiff( $otitle, $ntitle, $notice =
'' ) {
939 Hooks::run(
'DifferenceEngineShowDiff', [ $this ] );
941 $diff = $this->
getDiff( $otitle, $ntitle, $notice );
942 if ( $diff ===
false ) {
958 if ( !$this->isSlotDiffRenderer ) {
960 'mediawiki.interface.helpers.styles',
961 'mediawiki.diff.styles'
964 $slotDiffRenderer->addModules( $this->
getOutput() );
978 public function getDiff( $otitle, $ntitle, $notice =
'' ) {
980 if ( $body ===
false ) {
986 if ( $body ===
'' ) {
987 $notice .=
'<div class="mw-diff-empty">' .
988 $this->
msg(
'diff-empty' )->parse() .
992 return $this->
addHeader( $body, $otitle, $ntitle, $multi, $notice );
1001 $this->mCacheHit =
true;
1003 if ( !$this->isContentOverridden ) {
1006 } elseif ( $this->mOldRev &&
1010 } elseif ( $this->mNewRev &&
1016 if ( $this->mOldRev ===
false || ( $this->mOldRev && $this->mNewRev &&
1017 $this->mOldRev->getId() && $this->mOldRev->getId() == $this->mNewRev->getId() )
1019 if (
Hooks::run(
'DifferenceEngineShowEmptyOldContent', [ $this ] ) ) {
1027 $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
1028 if ( $this->mOldid && $this->mNewid ) {
1032 if ( $key ===
null ) {
1037 if ( !$this->mRefreshCache ) {
1038 $difftext =
$cache->get( $key );
1039 if ( is_string( $difftext ) ) {
1042 $difftext .=
"\n<!-- diff cache key $key -->\n";
1048 $this->mCacheHit =
false;
1060 $slotDiff = $slotDiffRenderer->getDiff( $slotContents[$role][
'old'],
1061 $slotContents[$role][
'new'] );
1062 if ( $slotDiff && $role !== SlotRecord::MAIN ) {
1067 $difftext .= $slotDiff;
1071 $diffEngine = $this;
1074 if ( !
Hooks::run(
'AbortDiffCache', [ &$diffEngine ] ) ) {
1076 } elseif ( $key !==
false && $difftext !==
false ) {
1078 $cache->set( $key, $difftext, 7 * 86400 );
1083 if ( $difftext !==
false ) {
1098 if ( !isset( $diffRenderers[$role] ) ) {
1103 $slotDiff = $diffRenderers[$role]->getDiff( $slotContents[$role][
'old'],
1104 $slotContents[$role][
'new'] );
1109 if ( $role !== SlotRecord::MAIN ) {
1127 $columnCount = $this->mOldRev ? 4 : 2;
1129 return Html::rawElement(
'tr', [
'class' =>
'mw-diff-slot-header',
'lang' => $userLang ],
1130 Html::element(
'th', [
'colspan' => $columnCount ], $headerText ) );
1160 if ( !$this->mOldid || !$this->mNewid ) {
1161 throw new MWException(
'mOldid and mNewid must be set to get diff cache key.' );
1169 "old-{$this->mOldid}",
1170 "rev-{$this->mNewid}"
1173 if (
$engine ===
'wikidiff2' ) {
1174 $params[] = phpversion(
'wikidiff2' );
1175 $params[] = $this->
getConfig()->get(
'WikiDiff2MovedParagraphDetectionCutoff' );
1178 if ( !$this->isSlotDiffRenderer ) {
1180 $params = array_merge(
$params, $slotDiffRenderer->getExtraCacheKeys() );
1198 $this->mOldid = 123456789;
1199 $this->mNewid = 987654321;
1203 if ( $cacheString ) {
1204 return [ $cacheString ];
1217 if ( array_slice(
$params, 0,
count( $standardParams ) ) === $standardParams ) {
1241 && $this->isSlotDiffRenderer
1247 throw new Exception( get_class( $this ) .
': could not maintain backwards compatibility. '
1248 .
'Please use a SlotDiffRenderer.' );
1250 return $slotDiffRenderer->getDiff( $old, $new ) . $this->
getDebugString();
1267 ->getSlotDiffRenderer( $this->
getContext() );
1271 throw new Exception(
'The slot diff renderer for text content should be a '
1272 .
'TextSlotDiffRenderer subclass' );
1274 return $slotDiffRenderer->getTextDiff( $otext, $ntext ) . $this->
getDebugString();
1288 wfDeprecated(
"\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'",
'1.27' );
1291 wfDeprecated(
"\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'",
'1.32' );
1295 wfWarn(
'$wgExternalDiffEngine is set to a non-string value, forcing it to false' );
1323 ->getSlotDiffRenderer( $this->
getContext() );
1327 throw new Exception(
'The slot diff renderer for text content should be a '
1328 .
'TextSlotDiffRenderer subclass' );
1330 return $slotDiffRenderer->getTextDiff( $otext, $ntext ) . $this->
getDebugString();
1342 if ( !$this->enableDebugComment ) {
1346 if ( $this->
getConfig()->
get(
'ShowHostnames' ) ) {
1351 return "<!-- diff generator: " .
1352 implode(
" ", array_map(
"htmlspecialchars",
$data ) ) .
1358 if (
$engine ===
'wikidiff2' ) {
1359 return $this->
debug(
'wikidiff2' );
1360 } elseif (
$engine ===
false ) {
1361 return $this->
debug(
'native PHP' );
1363 return $this->
debug(
"external $engine" );
1375 if ( $this->
getEngine() ===
'wikidiff2' &&
1376 version_compare( phpversion(
'wikidiff2' ),
'1.5.1',
'>=' )
1391 return preg_replace_callback(
1392 '/<!--LINE (\d+)-->/',
1393 [ $this,
'localiseLineNumbersCb' ],
1399 if (
$matches[1] ===
'1' && $this->mReducedLineNumbers ) {
1403 return $this->
msg(
'lineno' )->numParams(
$matches[1] )->escaped();
1413 return preg_replace_callback(
1414 '/class="mw-diff-movedpara-(left|right)"/',
1415 [ $this,
'addLocalisedTitleTooltipsCb' ],
1426 'diff-paragraph-moved-toold' :
1427 'diff-paragraph-moved-tonew';
1428 return $matches[0] .
' title="' . $this->
msg( $key )->escaped() .
'"';
1439 !$this->mOldRev || !$this->mNewRev
1440 || !$this->mOldPage || !$this->mNewPage
1441 || !$this->mOldPage->equals( $this->mNewPage )
1446 if ( $this->mOldRev->getTimestamp() > $this->mNewRev->getTimestamp() ) {
1456 $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev,
$newRev, 1000 );
1457 if ( $nEdits > 0 && $nEdits <= 1000 ) {
1459 $users = $this->mNewPage->getAuthorsBetween( $oldRev,
$newRev, $limit );
1460 $numUsers =
count( $users );
1482 if ( $numUsers === 0 ) {
1483 $msg =
'diff-multi-sameuser';
1484 } elseif ( $numUsers > $limit ) {
1485 $msg =
'diff-multi-manyusers';
1488 $msg =
'diff-multi-otherusers';
1491 return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse();
1506 $revtimestamp =
$rev->getTimestamp();
1507 $timestamp =
$lang->userTimeAndDate( $revtimestamp,
$user );
1508 $dateofrev =
$lang->userDate( $revtimestamp,
$user );
1509 $timeofrev =
$lang->userTime( $revtimestamp,
$user );
1512 $rev->
isCurrent() ?
'currentrev-asof' :
'revisionasof',
1518 if ( $complete !==
'complete' ) {
1525 [
'oldid' =>
$rev->getId() ] );
1528 $editQuery = [
'action' =>
'edit' ];
1529 if ( !
$rev->isCurrent() ) {
1530 $editQuery[
'oldid'] =
$rev->getId();
1533 $key =
$title->quickUserCan(
'edit',
$user ) ?
'editold' :
'viewsourceold';
1534 $msg = $this->
msg( $key )->escaped();
1535 $editLink = $this->
msg(
'parentheses' )->rawParams(
1537 $header .=
' ' . Html::rawElement(
1539 [
'class' =>
'mw-diff-edit' ],
1545 [
'class' =>
'history-deleted' ],
1550 $header = Html::rawElement(
'span', [
'class' =>
'history-deleted' ],
$header );
1568 public function addHeader( $diff, $otitle, $ntitle, $multi =
'', $notice =
'' ) {
1571 $header = Html::openElement(
'table', [
1572 'class' => [
'diff',
'diff-contentalign-' . $this->
getDiffLang()->alignStart() ],
1573 'data-mw' =>
'interface',
1575 $userLang = htmlspecialchars( $this->
getLanguage()->getHtmlCode() );
1577 if ( !$diff && !$otitle ) {
1579 <tr class=\"diff-title\" lang=\"{$userLang}\">
1580 <td class=\"diff-ntitle\">{$ntitle}</td>
1586 <col class=\"diff-marker\" />
1587 <col class=\"diff-content\" />
1588 <col class=\"diff-marker\" />
1589 <col class=\"diff-content\" />";
1596 if ( $otitle || $ntitle ) {
1598 <tr class=\"diff-title\" lang=\"{$userLang}\">
1599 <td colspan=\"$colspan\" class=\"diff-otitle\">{$otitle}</td>
1600 <td colspan=\"$colspan\" class=\"diff-ntitle\">{$ntitle}</td>
1605 if ( $multi !=
'' ) {
1606 $header .=
"<tr><td colspan=\"{$multiColspan}\" " .
1607 "class=\"diff-multi\" lang=\"{$userLang}\">{$multi}</td></tr>";
1609 if ( $notice !=
'' ) {
1610 $header .=
"<tr><td colspan=\"{$multiColspan}\" " .
1611 "class=\"diff-notice\" lang=\"{$userLang}\">{$notice}</td></tr>";
1614 return $header . $diff .
"</table>";
1625 $this->mOldContent = $oldContent;
1626 $this->mNewContent = $newContent;
1628 $this->mTextLoaded = 2;
1629 $this->mRevisionsLoaded =
true;
1630 $this->isContentOverridden =
true;
1631 $this->slotDiffRenderers =
null;
1642 if ( $oldRevision ) {
1643 $this->mOldRev =
new Revision( $oldRevision );
1644 $this->mOldid = $oldRevision->getId();
1648 $this->mOldContent = $oldRevision->getContent( SlotRecord::MAIN,
1649 RevisionRecord::FOR_THIS_USER, $this->
getUser() );
1651 $this->mOldPage =
null;
1652 $this->mOldRev = $this->mOldid =
false;
1654 $this->mNewRev =
new Revision( $newRevision );
1655 $this->mNewid = $newRevision->
getId();
1657 $this->mNewContent = $newRevision->
getContent( SlotRecord::MAIN,
1658 RevisionRecord::FOR_THIS_USER, $this->
getUser() );
1660 $this->mRevisionsIdsLoaded = $this->mRevisionsLoaded =
true;
1661 $this->mTextLoaded = $oldRevision ? 2 : 1;
1662 $this->isContentOverridden =
false;
1663 $this->slotDiffRenderers =
null;
1673 $this->mDiffLang =
$lang;
1688 if ( $new ===
'prev' ) {
1690 $newid = intval( $old );
1691 $oldid = $this->
getTitle()->getPreviousRevisionID( $newid );
1692 } elseif ( $new ===
'next' ) {
1694 $oldid = intval( $old );
1695 $newid = $this->
getTitle()->getNextRevisionID( $oldid );
1697 $oldid = intval( $old );
1698 $newid = intval( $new );
1701 return [ $oldid, $newid ];
1708 if ( $this->mRevisionsIdsLoaded ) {
1712 $this->mRevisionsIdsLoaded =
true;
1718 if ( $new ===
'next' && $this->mNewid ===
false ) {
1719 # if no result, NewId points to the newest old revision. The only newer
1720 # revision is cur, which is "0".
1725 'NewDifferenceEngine',
1726 [ $this->
getTitle(), &$this->mOldid, &$this->mNewid, $old, $new ]
1744 if ( $this->mRevisionsLoaded ) {
1745 return $this->isContentOverridden || $this->mNewRev && !is_null( $this->mOldRev );
1749 $this->mRevisionsLoaded =
true;
1754 if ( $this->mNewid ) {
1760 Revision::READ_NORMAL
1764 if ( !$this->mNewRev instanceof
Revision ) {
1769 $this->mNewid = $this->mNewRev->getId();
1770 if ( $this->mNewid ) {
1771 $this->mNewPage = $this->mNewRev->getTitle();
1773 $this->mNewPage =
null;
1777 $this->mOldRev =
false;
1778 if ( $this->mOldid ) {
1780 } elseif ( $this->mOldid === 0 ) {
1781 $rev = $this->mNewRev->getPrevious();
1783 $this->mOldid =
$rev->getId();
1784 $this->mOldRev =
$rev;
1787 $this->mOldid =
false;
1788 $this->mOldRev =
false;
1792 if ( is_null( $this->mOldRev ) ) {
1796 if ( $this->mOldRev && $this->mOldRev->getId() ) {
1797 $this->mOldPage = $this->mOldRev->getTitle();
1799 $this->mOldPage =
null;
1804 $changeTagDefStore = MediaWikiServices::getInstance()->getChangeTagDefStore();
1805 if ( $this->mOldid !==
false ) {
1806 $tagIds =
$dbr->selectFieldValues(
1809 [
'ct_rev_id' => $this->mOldid ],
1813 foreach ( $tagIds
as $tagId ) {
1815 $tags[] = $changeTagDefStore->getName( (
int)$tagId );
1820 $this->mOldTags = implode(
',', $tags );
1822 $this->mOldTags =
false;
1825 $tagIds =
$dbr->selectFieldValues(
1828 [
'ct_rev_id' => $this->mNewid ],
1832 foreach ( $tagIds
as $tagId ) {
1834 $tags[] = $changeTagDefStore->getName( (
int)$tagId );
1839 $this->mNewTags = implode(
',', $tags );
1853 if ( $this->mTextLoaded == 2 ) {
1855 && $this->mNewContent;
1859 $this->mTextLoaded = 2;
1865 if ( $this->mOldRev ) {
1867 if ( $this->mOldContent ===
null ) {
1873 Hooks::run(
'DifferenceEngineLoadTextAfterNewContentIsLoaded', [ $this ] );
1874 if ( $this->mNewContent ===
null ) {
1887 if ( $this->mTextLoaded >= 1 ) {
1891 $this->mTextLoaded = 1;
1899 Hooks::run(
'DifferenceEngineAfterLoadNewText', [ $this ] );
getContentHandler()
Convenience method that returns the ContentHandler singleton for handling the content model that this...
static newFromArchiveRow( $row, $overrides=[])
Make a fake revision object from an archive table row.
bool $mRevisionsIdsLoaded
Have the revisions IDs been loaded.
revisionDeleteLink( $rev)
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
getSlotContents()
Get the old and new content objects for all slots.
return true to allow those checks to and false if checking is done & $user
wfMergeErrorArrays(... $args)
Merge arrays in the style of getUserPermissionsErrors, with duplicate removal e.g.
markPatrolledLink()
Build a link to mark a change as patrolled.
getContext()
Get the base IContextSource object.
static getEngine()
Process $wgExternalDiffEngine and get a sane, usable engine.
int $mTextLoaded
How many text blobs have been loaded, 0, 1 or 2?
addLocalisedTitleTooltipsCb(array $matches)
getDiffBodyCacheKeyParams()
Get the cache key parameters.
static newFromId( $id, $flags=0)
Load a page revision from a given revision ID number.
bool $unhide
Show rev_deleted content if allowed.
if(!isset( $args[0])) $lang
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
setReducedLineNumbers( $value=true)
Set reduced line numbers mode.
also included in $newHeader if any $newminor
msg( $key)
Get a Message object with context set Parameters are the same as wfMessage()
setContent(Content $oldContent, Content $newContent)
Use specified text instead of loading from the database.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
getNewid()
Get the ID of new revision (right pane) of the diff.
getOldRevision()
Get the left side of the diff.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not it can be in the form of< username >< more info > e g for bot passwords intended to be added to log contexts Fields it might only if the login was with a bot password it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
getOldid()
Get the ID of old revision (left pane) of the diff.
Class representing a MediaWiki article and history.
bool $mRevisionsLoaded
Have the revisions been loaded.
deletedIdMarker( $id)
Build a wikitext link toward a deleted revision, if viewable.
static revComment(Revision $rev, $local=false, $isPublic=false, $useParentheses=true)
Wrap and format the given revision's comment block, if the current user is allowed to view it.
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
wfHostname()
Fetch server name for use in error reporting etc.
static linkKnown( $target, $html=null, $customAttribs=[], $query=[], $options=[ 'known'])
Identical to link(), except $options defaults to 'known'.
bool $isSlotDiffRenderer
Temporary hack for B/C while slot diff related methods of DifferenceEngine are being deprecated.
static getArchiveQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new archived revision objec...
static getTitleFor( $name, $subpage=false, $fragment='')
Get a localised Title object for a specified special page name If you don't need a full Title object,...
addLocalisedTitleTooltips( $text)
Add title attributes for tooltips on moved paragraph indicators.
Show an error when a user tries to do something they do not have the necessary permissions for.
showDiffStyle()
Add style sheets for diff display.
loadNewText()
Load the text of the new revision, not the old one.
showDiff( $otitle, $ntitle, $notice='')
Get the diff text, send it to the OutputPage object Returns false if the diff could not be generated,...
getDiffBodyCacheKey()
Returns the cache key for diff body text or content.
localiseDiff( $text)
Localise diff output.
int string false null $mNewid
Revision ID for the new revision.
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
generateTextDiffBody( $otext, $ntext)
Generate a diff, no caching.
bool $mReducedLineNumbers
If true, line X is not displayed when X is 1, for example to increase readability and conserve space ...
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target.
null for the wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify $query
$data
Utility to generate mapping file used in mw.Title (phpCharToUpper.json)
loadRevisionData()
Load revision metadata for the specified revisions.
localiseLineNumbersCb( $matches)
namespace and then decline to actually register it file or subcat img or subcat $title
static factory(Title $title)
Create a WikiPage object of the appropriate class for the given title.
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
getDiffBodyForRole( $role)
Get the diff table body for one slot, without header.
SlotDiffRenderer[] $slotDiffRenderers
DifferenceEngine classes for the slots, keyed by role name.
static generateRollback( $rev, IContextSource $context=null, $options=[ 'verify'])
Generate a rollback link for a given revision.
string[] null $mNewTags
Change tags of $mNewRev or null if it does not exist / is not saved.
wfIncrStats( $key, $count=1)
Increment a statistics counter.
getDiffLang()
Get the language of the difference engine, defaults to page content language.
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
getParserOutput(ParserOptions $parserOptions, $oldid=null, $forceParse=false)
Get a ParserOutput for the given ParserOptions and revision ID.
getSlotHeader( $headerText)
Get a slot header for inclusion in a diff body (as a table row).
The simplest way of implementing IContextSource is to hold a RequestContext as a member variable and ...
Revision null false $mOldRev
Old revision (left pane).
getWikiPage()
Get the WikiPage object.
Content null $mNewContent
deletedLink( $id)
Look up a special:Undelete link to the given deleted revision id, as a workaround for being unable to...
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
static flag( $flag, IContextSource $context=null)
Make an "<abbr>" element for a given change flag.
getDiffBody()
Get the diff table body, without header.
getRevisionHeader(Revision $rev, $complete='')
Get a header for a specified revision.
the value to return A Title object or null for latest all implement SearchIndexField $engine
static makeTitle( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
loadText()
Load the text of the revisions, as well as revision data.
static revUserTools( $rev, $isPublic=false, $useParentheses=true)
Generate a user tool link cluster if the current user is allowed to view it.
The wiki should then use memcached to cache various data To use multiple just add more items to the array To increase the weight of a make its entry a array("192.168.0.1:11211", 2))
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
setContext(IContextSource $context)
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
bool $mCacheHit
Was the diff fetched from cache?
deprecatePublicProperty( $property, $version, $class=null, $component=null)
Mark a property as deprecated.
also included in $newHeader $rollback
$wgExternalDiffEngine
Name of the external diff engine to use.
markAsSlotDiffRenderer()
Mark this DifferenceEngine as a slot renderer (as opposed to a page renderer).
$enableDebugComment
Set this to true to add debug info to the HTML output.
__construct( $context=null, $old=0, $new=0, $rcid=0, $refreshCache=false, $unhide=false)
#-
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
setTextLanguage(Language $lang)
Set the language in which the diff text is written.
string[] null $mOldTags
Change tags of $mOldRev or null if it does not exist / is not saved.
static getRevDeleteLink(User $user, Revision $rev, Title $title)
Get a revision-deletion link, or disabled link, or nothing, depending on user permissions & the setti...
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging a wrapping ErrorException $suppressed
null for the wiki Added should default to null in handler for backwards compatibility add a value to it if you want to add a cookie that have to vary cache options can modify as strings Extensions should add to this list prev or next $refreshCache
showDiffPage( $diffOnly=false)
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
int false null $mOldid
Revision ID for the old revision.
static titleAttrib( $name, $options=null, array $msgParams=[])
Given the id of an interface element, constructs the appropriate title attribute from the system mess...
bool $isContentOverridden
Was the content overridden via setContent()? If the content was overridden, most internal state (e....
const DIFF_VERSION
Constant to indicate diff cache compatibility.
Interface for objects which can provide a MediaWiki context on request.
static intermediateEditsMsg( $numEdits, $numUsers, $limit)
Get a notice about how many intermediate edits and users there are.
Base interface for content objects.
loadRevisionIds()
Load revision IDs.
getParserOutput(WikiPage $page, Revision $rev)
getNewRevision()
Get the right side of the diff.
Represents a title within MediaWiki.
Renders a diff for a single slot (that is, a diff between two content objects).
setRevisions(RevisionRecord $oldRevision=null, RevisionRecord $newRevision)
Use specified text instead of loading from the database.
getMarkPatrolledLinkInfo()
Returns an array of meta data needed to build a "mark as patrolled" link and adds the mediawiki....
Title null $mOldPage
Title of $mOldRev or null if the old revision does not exist or does not belong to a page.
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
getExtraCacheKeys()
Implements DifferenceEngineSlotDiffRenderer::getExtraCacheKeys().
DifferenceEngine is responsible for rendering the difference between two revisions as HTML.
B/C adapter for turning a DifferenceEngine into a SlotDiffRenderer.
static isInRCLifespan( $timestamp, $tolerance=0)
Check whether the given timestamp is new enough to have a RC row with a given tolerance as the recent...
usually copyright or history_copyright This message must be in HTML not wikitext & $link
passed in as a query string parameter to the various URLs constructed here(i.e. $nextlink) $rdel also included in $oldHeader $oldminor
string $mMarkPatrolledLink
Link to action=markpatrolled.
localiseLineNumbers( $text)
Replace line numbers with the text in the user's language.
Content null $mOldContent
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
debug( $generator="internal")
Generate a debug comment indicating diff generating time, server node, and generator backend.
renderNewRevision()
Show the new revision of the page.
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency MediaWikiServices
getDiff( $otitle, $ntitle, $notice='')
Get complete diff table, including header.
either a unescaped string or a HtmlArmor object after in associative array form externallinks including delete and has completed for all link tables whether this was an auto creation use $formDescriptor instead default is conds Array Extra conditions for the No matching items in log is displayed if loglist is empty msgKey Array If you want a nice box with a set this to the key of the message First element is the message additional optional elements are parameters for the key that are processed with wfMessage() -> params() ->parseAsBlock() - offset Set to overwrite offset parameter in $wgRequest set to '' to unset offset - wrap String Wrap the message in html(usually something like "<
Revision null $mNewRev
New revision (right pane).
trait DeprecationHelper
Use this trait in classes which have properties for which public access is deprecated.
generateContentDiffBody(Content $old, Content $new)
Generate a diff, no caching.
mapDiffPrevNext( $old, $new)
Maps a revision pair definition as accepted by DifferenceEngine constructor to a pair of actual integ...
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
addHeader( $diff, $otitle, $ntitle, $multi='', $notice='')
Add the header to a diff body.
Internationalisation code.
textDiff( $otext, $ntext)
Generates diff, to be wrapped internally in a logging/instrumentation.
Title null $mNewPage
Title of $mNewRev or null if the new revision does not exist or does not belong to a page.
Renders a slot diff by doing a text diff on the native representation.
bool $mRefreshCache
Refresh the diff cache.
getMultiNotice()
If there are revisions between the ones being compared, return a note saying so.