From d9022f63880ce039446fba8364f68e656b7bf4cb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 3 May 2012 13:01:35 +0200 Subject: Update to MediaWiki 1.19.0 --- includes/diff/DairikiDiff.php | 187 +++++++- includes/diff/DifferenceEngine.php | 913 +++++++++++++++++-------------------- includes/diff/WikiDiff3.php | 5 +- 3 files changed, 604 insertions(+), 501 deletions(-) (limited to 'includes/diff') diff --git a/includes/diff/DairikiDiff.php b/includes/diff/DairikiDiff.php index 8f19712b..c935eee2 100644 --- a/includes/diff/DairikiDiff.php +++ b/includes/diff/DairikiDiff.php @@ -24,10 +24,16 @@ class _DiffOp { trigger_error( 'pure virtual', E_USER_ERROR ); } + /** + * @return int + */ function norig() { return $this->orig ? sizeof( $this->orig ) : 0; } + /** + * @return int + */ function nclosing() { return $this->closing ? sizeof( $this->closing ) : 0; } @@ -49,6 +55,9 @@ class _DiffOp_Copy extends _DiffOp { $this->closing = $closing; } + /** + * @return _DiffOp_Copy + */ function reverse() { return new _DiffOp_Copy( $this->closing, $this->orig ); } @@ -67,6 +76,9 @@ class _DiffOp_Delete extends _DiffOp { $this->closing = false; } + /** + * @return _DiffOp_Add + */ function reverse() { return new _DiffOp_Add( $this->orig ); } @@ -85,6 +97,9 @@ class _DiffOp_Add extends _DiffOp { $this->orig = false; } + /** + * @return _DiffOp_Delete + */ function reverse() { return new _DiffOp_Delete( $this->closing ); } @@ -103,6 +118,9 @@ class _DiffOp_Change extends _DiffOp { $this->closing = $closing; } + /** + * @return _DiffOp_Change + */ function reverse() { return new _DiffOp_Change( $this->closing, $this->orig ); } @@ -145,6 +163,11 @@ class _DiffEngine { protected $lcs = 0; + /** + * @param $from_lines + * @param $to_lines + * @return array + */ function diff ( $from_lines, $to_lines ) { wfProfileIn( __METHOD__ ); @@ -199,6 +222,10 @@ class _DiffEngine { return $edits; } + /** + * @param $from_lines + * @param $to_lines + */ function diff_local ( $from_lines, $to_lines ) { global $wgExternalDiffEngine; wfProfileIn( __METHOD__ ); @@ -268,6 +295,8 @@ class _DiffEngine { /** * Returns the whole line if it's small enough, or the MD5 hash otherwise + * @param $line string + * @return string */ function _line_hash( $line ) { if ( strlen( $line ) > self::MAX_XREF_LENGTH ) { @@ -293,6 +322,12 @@ class _DiffEngine { * of the two files do not match, and likewise that the last lines do not * match. The caller must trim matching lines from the beginning and end * of the portions it is going to specify. + * @param $xoff + * @param $xlim + * @param $yoff + * @param $ylim + * @param $nchunks + * @return array */ function _diag( $xoff, $xlim, $yoff, $ylim, $nchunks ) { $flip = false; @@ -373,6 +408,10 @@ class _DiffEngine { return array( $this->lcs, $seps ); } + /** + * @param $ypos + * @return int + */ function _lcs_pos( $ypos ) { $end = $this->lcs; if ( $end == 0 || $ypos > $this->seq[$end] ) { @@ -410,6 +449,10 @@ class _DiffEngine { * * Note that XLIM, YLIM are exclusive bounds. * All line numbers are origin-0 and discarded lines are not counted. + * @param $xoff + * @param $xlim + * @param $yoff + * @param $ylim */ function _compareseq ( $xoff, $xlim, $yoff, $ylim ) { // Slide down the bottom initial diagonal. @@ -703,6 +746,8 @@ class Diff { * Check a Diff for validity. * * This is here only for debugging purposes. + * @param $from_lines + * @param $to_lines */ function _check( $from_lines, $to_lines ) { wfProfileIn( __METHOD__ ); @@ -886,6 +931,13 @@ class DiffFormatter { return $end; } + /** + * @param $xbeg + * @param $xlen + * @param $ybeg + * @param $ylen + * @param $edits + */ function _block( $xbeg, $xlen, $ybeg, $ylen, &$edits ) { wfProfileIn( __METHOD__ ); $this->_start_block( $this->_block_header( $xbeg, $xlen, $ybeg, $ylen ) ); @@ -910,12 +962,22 @@ class DiffFormatter { ob_start(); } + /** + * @return string + */ function _end_diff() { $val = ob_get_contents(); ob_end_clean(); return $val; } + /** + * @param $xbeg + * @param $xlen + * @param $ybeg + * @param $ylen + * @return string + */ function _block_header( $xbeg, $xlen, $ybeg, $ylen ) { if ( $xlen > 1 ) { $xbeg .= ',' . ( $xbeg + $xlen - 1 ); @@ -934,23 +996,41 @@ class DiffFormatter { function _end_block() { } + /** + * @param $lines + * @param $prefix string + */ function _lines( $lines, $prefix = ' ' ) { foreach ( $lines as $line ) { echo "$prefix $line\n"; } } + /** + * @param $lines + */ function _context( $lines ) { $this->_lines( $lines ); } + /** + * @param $lines + */ function _added( $lines ) { $this->_lines( $lines, '>' ); } + + /** + * @param $lines + */ function _deleted( $lines ) { $this->_lines( $lines, '<' ); } + /** + * @param $orig + * @param $closing + */ function _changed( $orig, $closing ) { $this->_deleted( $orig ); echo "---\n"; @@ -966,16 +1046,36 @@ class UnifiedDiffFormatter extends DiffFormatter { var $leading_context_lines = 2; var $trailing_context_lines = 2; + /** + * @param $lines + */ function _added( $lines ) { $this->_lines( $lines, '+' ); } + + /** + * @param $lines + */ function _deleted( $lines ) { $this->_lines( $lines, '-' ); } + + /** + * @param $orig + * @param $closing + */ function _changed( $orig, $closing ) { $this->_deleted( $orig ); $this->_added( $closing ); } + + /** + * @param $xbeg + * @param $xlen + * @param $ybeg + * @param $ylen + * @return string + */ function _block_header( $xbeg, $xlen, $ybeg, $ylen ) { return "@@ -$xbeg,$xlen +$ybeg,$ylen @@"; } @@ -986,6 +1086,11 @@ class UnifiedDiffFormatter extends DiffFormatter { * @ingroup DifferenceEngine */ class ArrayDiffFormatter extends DiffFormatter { + + /** + * @param $diff + * @return array + */ function format( $diff ) { $oldline = 1; $newline = 1; @@ -1049,6 +1154,9 @@ class _HWLDF_WordAccumulator { $this->_tag = ''; } + /** + * @param $new_tag + */ function _flushGroup( $new_tag ) { if ( $this->_group !== '' ) { if ( $this->_tag == 'ins' ) { @@ -1065,6 +1173,9 @@ class _HWLDF_WordAccumulator { $this->_tag = $new_tag; } + /** + * @param $new_tag + */ function _flushLine( $new_tag ) { $this->_flushGroup( $new_tag ); if ( $this->_line != '' ) { @@ -1076,6 +1187,10 @@ class _HWLDF_WordAccumulator { $this->_line = ''; } + /** + * @param $words + * @param $tag string + */ function addWords ( $words, $tag = '' ) { if ( $tag != $this->_tag ) { $this->_flushGroup( $tag ); @@ -1095,6 +1210,9 @@ class _HWLDF_WordAccumulator { } } + /** + * @return array + */ function getLines() { $this->_flushLine( '~done' ); return $this->_lines; @@ -1109,6 +1227,10 @@ class _HWLDF_WordAccumulator { class WordLevelDiff extends MappedDiff { const MAX_LINE_LENGTH = 10000; + /** + * @param $orig_lines + * @param $closing_lines + */ function __construct ( $orig_lines, $closing_lines ) { wfProfileIn( __METHOD__ ); @@ -1120,6 +1242,10 @@ class WordLevelDiff extends MappedDiff { wfProfileOut( __METHOD__ ); } + /** + * @param $lines + * @return array + */ function _split( $lines ) { wfProfileIn( __METHOD__ ); @@ -1152,6 +1278,9 @@ class WordLevelDiff extends MappedDiff { return array( $words, $stripped ); } + /** + * @return array + */ function orig() { wfProfileIn( __METHOD__ ); $orig = new _HWLDF_WordAccumulator; @@ -1168,6 +1297,9 @@ class WordLevelDiff extends MappedDiff { return $lines; } + /** + * @return array + */ function closing() { wfProfileIn( __METHOD__ ); $closing = new _HWLDF_WordAccumulator; @@ -1197,6 +1329,11 @@ class TableDiffFormatter extends DiffFormatter { $this->trailing_context_lines = 2; } + /** + * @static + * @param $msg + * @return mixed + */ public static function escapeWhiteSpace( $msg ) { $msg = preg_replace( '/^ /m', '  ', $msg ); $msg = preg_replace( '/ $/m', '  ', $msg ); @@ -1204,12 +1341,22 @@ class TableDiffFormatter extends DiffFormatter { return $msg; } + /** + * @param $xbeg + * @param $xlen + * @param $ybeg + * @param $ylen + * @return string + */ function _block_header( $xbeg, $xlen, $ybeg, $ylen ) { $r = '\n" . '\n"; return $r; } + /** + * @param $header + */ function _start_block( $header ) { echo $header; } @@ -1220,21 +1367,39 @@ class TableDiffFormatter extends DiffFormatter { function _lines( $lines, $prefix = ' ', $color = 'white' ) { } - # HTML-escape parameter before calling this + /** + * HTML-escape parameter before calling this + * @param $line + * @return string + */ function addedLine( $line ) { return $this->wrapLine( '+', 'diff-addedline', $line ); } - # HTML-escape parameter before calling this + /** + * HTML-escape parameter before calling this + * @param $line + * @return string + */ function deletedLine( $line ) { return $this->wrapLine( '−', 'diff-deletedline', $line ); } - # HTML-escape parameter before calling this + /** + * HTML-escape parameter before calling this + * @param $line + * @return string + */ function contextLine( $line ) { return $this->wrapLine( ' ', 'diff-context', $line ); } + /** + * @param $marker + * @param $class + * @param $line + * @return string + */ private function wrapLine( $marker, $class, $line ) { if ( $line !== '' ) { // The
wrapper is needed for 'overflow: auto' style to scroll properly @@ -1243,10 +1408,16 @@ class TableDiffFormatter extends DiffFormatter { return "$marker$line"; } + /** + * @return string + */ function emptyLine() { return ' '; } + /** + * @param $lines array + */ function _added( $lines ) { foreach ( $lines as $line ) { echo '' . $this->emptyLine() . @@ -1255,6 +1426,9 @@ class TableDiffFormatter extends DiffFormatter { } } + /** + * @param $lines + */ function _deleted( $lines ) { foreach ( $lines as $line ) { echo '' . $this->deletedLine( '' . @@ -1263,6 +1437,9 @@ class TableDiffFormatter extends DiffFormatter { } } + /** + * @param $lines + */ function _context( $lines ) { foreach ( $lines as $line ) { echo '' . @@ -1271,6 +1448,10 @@ class TableDiffFormatter extends DiffFormatter { } } + /** + * @param $orig + * @param $closing + */ function _changed( $orig, $closing ) { wfProfileIn( __METHOD__ ); diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php index 5902461b..439e3204 100644 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@ -18,24 +18,25 @@ define( 'MW_DIFF_VERSION', '1.11a' ); * @todo document * @ingroup DifferenceEngine */ -class DifferenceEngine { +class DifferenceEngine extends ContextSource { /**#@+ * @private */ var $mOldid, $mNewid; - var $mOldtitle, $mNewtitle, $mPagetitle; var $mOldtext, $mNewtext; + protected $mDiffLang; /** * @var Title */ - var $mOldPage, $mNewPage, $mTitle; + var $mOldPage, $mNewPage; var $mRcidMarkPatrolled; /** * @var Revision */ var $mOldRev, $mNewRev; + private $mRevisionsIdsLoaded = false; // Have the revisions IDs been loaded var $mRevisionsLoaded = false; // Have the revisions been loaded var $mTextLoaded = 0; // How many text blobs have been loaded, 0, 1 or 2? var $mCacheHit = false; // Was the diff fetched from cache? @@ -51,49 +52,32 @@ class DifferenceEngine { // readability and conserve space with many small diffs. protected $mReducedLineNumbers = false; + // Link to action=markpatrolled + protected $mMarkPatrolledLink = null; + protected $unhide = false; # show rev_deleted content if allowed /**#@-*/ /** * Constructor - * @param $titleObj Title object that the diff is associated with + * @param $context IContextSource context to use, anything else will be ignored * @param $old Integer old ID we want to show and diff with. * @param $new String either 'prev' or 'next'. * @param $rcid Integer ??? FIXME (default 0) * @param $refreshCache boolean If set, refreshes the diff cache * @param $unhide boolean If set, allow viewing deleted revs */ - function __construct( $titleObj = null, $old = 0, $new = 0, $rcid = 0, + function __construct( $context = null, $old = 0, $new = 0, $rcid = 0, $refreshCache = false, $unhide = false ) { - if ( $titleObj ) { - $this->mTitle = $titleObj; - } else { - global $wgTitle; - $this->mTitle = $wgTitle; // @TODO: get rid of this + if ( $context instanceof IContextSource ) { + $this->setContext( $context ); } + wfDebug( "DifferenceEngine old '$old' new '$new' rcid '$rcid'\n" ); - if ( 'prev' === $new ) { - # Show diff between revision $old and the previous one. - # Get previous one from DB. - $this->mNewid = intval( $old ); - $this->mOldid = $this->mTitle->getPreviousRevisionID( $this->mNewid ); - } elseif ( 'next' === $new ) { - # Show diff between revision $old and the next one. - # Get next one from DB. - $this->mOldid = intval( $old ); - $this->mNewid = $this->mTitle->getNextRevisionID( $this->mOldid ); - if ( false === $this->mNewid ) { - # if no result, NewId points to the newest old revision. The only newer - # revision is cur, which is "0". - $this->mNewid = 0; - } - } else { - $this->mOldid = intval( $old ); - $this->mNewid = intval( $new ); - wfRunHooks( 'NewDifferenceEngine', array( &$titleObj, &$this->mOldid, &$this->mNewid, $old, $new ) ); - } + $this->mOldid = $old; + $this->mNewid = $new; $this->mRcidMarkPatrolled = intval( $rcid ); # force it to be an integer $this->mRefreshCache = $refreshCache; $this->unhide = $unhide; @@ -107,10 +91,14 @@ class DifferenceEngine { } /** - * @return Title + * @return Language */ - function getTitle() { - return $this->mTitle; + function getDiffLang() { + if ( $this->mDiffLang === null ) { + # Default language in which the diff text is written. + $this->mDiffLang = $this->getTitle()->getPageLanguage(); + } + return $this->mDiffLang; } /** @@ -124,6 +112,7 @@ class DifferenceEngine { * @return int */ function getOldid() { + $this->loadRevisionIds(); return $this->mOldid; } @@ -131,6 +120,7 @@ class DifferenceEngine { * @return Bool|int */ function getNewid() { + $this->loadRevisionIds(); return $this->mNewid; } @@ -142,8 +132,7 @@ class DifferenceEngine { * @return mixed URL or false */ function deletedLink( $id ) { - global $wgUser; - if ( $wgUser->isAllowed( 'deletedhistory' ) ) { + if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { $dbr = wfGetDB( DB_SLAVE ); $row = $dbr->selectRow('archive', '*', array( 'ar_rev_id' => $id ), @@ -176,281 +165,214 @@ class DifferenceEngine { } function showDiffPage( $diffOnly = false ) { - global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol; wfProfileIn( __METHOD__ ); # Allow frames except in certain special cases - $wgOut->allowClickjacking(); - - # If external diffs are enabled both globally and for the user, - # we'll use the application/x-external-editor interface to call - # an external diff tool like kompare, kdiff3, etc. - if ( $wgUseExternalEditor && $wgUser->getOption( 'externaldiff' ) ) { - global $wgCanonicalServer, $wgScript, $wgLang; - $wgOut->disable(); - header ( "Content-type: application/x-external-editor; charset=UTF-8" ); - $url1 = $this->mTitle->getCanonical( array( - 'action' => 'raw', - 'oldid' => $this->mOldid - ) ); - $url2 = $this->mTitle->getCanonical( array( - 'action' => 'raw', - 'oldid' => $this->mNewid - ) ); - $special = $wgLang->getNsText( NS_SPECIAL ); - $control = <<getOutput(); + $out->allowClickjacking(); + $out->setRobotPolicy( 'noindex,nofollow' ); - $wgOut->setArticleFlag( false ); if ( !$this->loadRevisionData() ) { // Sounds like a deleted revision... Let's see what we can do. - $t = $this->mTitle->getPrefixedText(); - $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), + $t = $this->getTitle()->getPrefixedText(); + $d = $this->msg( 'missingarticle-diff', $this->deletedIdMarker( $this->mOldid ), - $this->deletedIdMarker( $this->mNewid ) ); - $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) ); - $wgOut->addWikiMsg( 'missing-article', "$t", "$d" ); + $this->deletedIdMarker( $this->mNewid ) )->escaped(); + $out->setPageTitle( $this->msg( 'errorpagetitle' ) ); + $out->addWikiMsg( 'missing-article', "$t", "$d" ); wfProfileOut( __METHOD__ ); return; } - wfRunHooks( 'DiffViewHeader', array( $this, $this->mOldRev, $this->mNewRev ) ); - - if ( $this->mNewRev->isCurrent() ) { - $wgOut->setArticleFlag( true ); + $user = $this->getUser(); + $permErrors = $this->mNewPage->getUserPermissionsErrors( 'read', $user ); + if ( $this->mOldPage ) { # mOldPage might not be set, see below. + $permErrors = wfMergeErrorArrays( $permErrors, + $this->mOldPage->getUserPermissionsErrors( 'read', $user ) ); } - - # mOldid is false if the difference engine is called with a "vague" query for - # a diff between a version V and its previous version V' AND the version V - # is the first version of that article. In that case, V' does not exist. - if ( $this->mOldid === false ) { - $this->showFirstRevision(); - $this->renderNewRevision(); // should we respect $diffOnly here or not? + if ( count( $permErrors ) ) { wfProfileOut( __METHOD__ ); - return; + throw new PermissionsError( 'read', $permErrors ); } - $oldTitle = $this->mOldPage->getPrefixedText(); - $newTitle = $this->mNewPage->getPrefixedText(); - if ( $oldTitle == $newTitle ) { - $wgOut->setPageTitle( $newTitle ); - } else { - $wgOut->setPageTitle( $oldTitle . ', ' . $newTitle ); - } - if ( $this->mNewPage->equals( $this->mOldPage ) ) { - $wgOut->setSubtitle( wfMsgExt( 'difference', array( 'parseinline' ) ) ); - } else { - $wgOut->setSubtitle( wfMsgExt( 'difference-multipage', array( 'parseinline' ) ) ); - } - $wgOut->setRobotPolicy( 'noindex,nofollow' ); + # If external diffs are enabled both globally and for the user, + # we'll use the application/x-external-editor interface to call + # an external diff tool like kompare, kdiff3, etc. + if ( ExternalEdit::useExternalEngine( $this->getContext(), 'diff' ) ) { + $urls = array( + 'File' => array( 'Extension' => 'wiki', 'URL' => + # This should be mOldPage, but it may not be set, see below. + $this->mNewPage->getCanonicalURL( array( + 'action' => 'raw', 'oldid' => $this->mOldid ) ) + ), + 'File2' => array( 'Extension' => 'wiki', 'URL' => + $this->mNewPage->getCanonicalURL( array( + 'action' => 'raw', 'oldid' => $this->mNewid ) ) + ), + ); + + $externalEditor = new ExternalEdit( $this->getContext(), $urls ); + $externalEditor->execute(); - if ( !$this->mOldPage->userCanRead() || !$this->mNewPage->userCanRead() ) { - $wgOut->loginToUse(); - $wgOut->output(); - $wgOut->disable(); wfProfileOut( __METHOD__ ); return; } - $sk = $wgUser->getSkin(); - if ( method_exists( $sk, 'suppressQuickbar' ) ) { - $sk->suppressQuickbar(); + $rollback = ''; + $undoLink = ''; + + $query = array(); + # Carry over 'diffonly' param via navigation links + if ( $diffOnly != $user->getBoolOption( 'diffonly' ) ) { + $query['diffonly'] = $diffOnly; } + # Cascade unhide param in links for easy deletion browsing + if ( $this->unhide ) { + $query['unhide'] = 1; + } + + # Check if one of the revisions is deleted/suppressed + $deleted = $suppressed = false; + $allowed = $this->mNewRev->userCan( Revision::DELETED_TEXT, $user ); - // Check if page is editable - $editable = $this->mNewRev->getTitle()->userCan( 'edit' ); - if ( $editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed( 'rollback' ) ) { - $wgOut->preventClickjacking(); - $rollback = '   ' . $sk->generateRollback( $this->mNewRev ); + # mOldRev is false if the difference engine is called with a "vague" query for + # a diff between a version V and its previous version V' AND the version V + # is the first version of that article. In that case, V' does not exist. + if ( $this->mOldRev === false ) { + $out->setPageTitle( $this->mNewPage->getPrefixedText() ); + $out->addSubtitle( $this->msg( 'difference' ) ); + $samePage = true; + $oldHeader = ''; } else { - $rollback = ''; - } + wfRunHooks( 'DiffViewHeader', array( $this, $this->mOldRev, $this->mNewRev ) ); - // Prepare a change patrol link, if applicable - if ( $wgUseRCPatrol && $this->mTitle->userCan( 'patrol' ) ) { - // If we've been given an explicit change identifier, use it; saves time - if ( $this->mRcidMarkPatrolled ) { - $rcid = $this->mRcidMarkPatrolled; - $rc = RecentChange::newFromId( $rcid ); - // Already patrolled? - $rcid = is_object( $rc ) && !$rc->getAttribute( 'rc_patrolled' ) ? $rcid : 0; + $sk = $this->getSkin(); + if ( method_exists( $sk, 'suppressQuickbar' ) ) { + $sk->suppressQuickbar(); + } + + if ( $this->mNewPage->equals( $this->mOldPage ) ) { + $out->setPageTitle( $this->mNewPage->getPrefixedText() ); + $out->addSubtitle( $this->msg( 'difference' ) ); + $samePage = true; } else { - // Look for an unpatrolled change corresponding to this diff - $db = wfGetDB( DB_SLAVE ); - $change = RecentChange::newFromConds( - array( - // Redundant user,timestamp condition so we can use the existing index - 'rc_user_text' => $this->mNewRev->getRawUserText(), - 'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ), - 'rc_this_oldid' => $this->mNewid, - 'rc_last_oldid' => $this->mOldid, - 'rc_patrolled' => 0 - ), - __METHOD__ - ); - if ( $change instanceof RecentChange ) { - $rcid = $change->mAttribs['rc_id']; - $this->mRcidMarkPatrolled = $rcid; - } else { - // None found - $rcid = 0; + $out->setPageTitle( $this->mOldPage->getPrefixedText() . ', ' . $this->mNewPage->getPrefixedText() ); + $out->addSubtitle( $this->msg( 'difference-multipage' ) ); + $samePage = false; + } + + if ( $samePage && $this->mNewPage->userCan( 'edit', $user ) ) { + if ( $this->mNewRev->isCurrent() && $this->mNewPage->userCan( 'rollback', $user ) ) { + $out->preventClickjacking(); + $rollback = '   ' . Linker::generateRollback( $this->mNewRev ); + } + if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) { + $undoLink = ' ' . $this->msg( 'parentheses' )->rawParams( + Html::element( 'a', array( + 'href' => $this->mNewPage->getLocalUrl( array( + 'action' => 'edit', + 'undoafter' => $this->mOldid, + 'undo' => $this->mNewid ) ), + 'title' => Linker::titleAttrib( 'undo' ) + ), + $this->msg( 'editundo' )->text() + ) )->escaped(); } } - // Build the link - if ( $rcid ) { - $wgOut->preventClickjacking(); - $token = $wgUser->editToken( $rcid ); - $patrol = ' [' . $sk->link( - $this->mTitle, - wfMsgHtml( 'markaspatrolleddiff' ), - array(), - array( - 'action' => 'markpatrolled', - 'rcid' => $rcid, - 'token' => $token, - ), - array( - 'known', - 'noclasses' - ) - ) . ']'; + + # Make "previous revision link" + if ( $samePage && $this->mOldRev->getPrevious() ) { + $prevlink = Linker::linkKnown( + $this->mOldPage, + $this->msg( 'previousdiff' )->escaped(), + array( 'id' => 'differences-prevlink' ), + array( 'diff' => 'prev', 'oldid' => $this->mOldid ) + $query + ); } else { - $patrol = ''; + $prevlink = ' '; } - } else { - $patrol = ''; - } - # Carry over 'diffonly' param via navigation links - if ( $diffOnly != $wgUser->getBoolOption( 'diffonly' ) ) { - $query['diffonly'] = $diffOnly; - } + if ( $this->mOldRev->isMinor() ) { + $oldminor = ChangesList::flag( 'minor' ); + } else { + $oldminor = ''; + } - # Make "previous revision link" - $query['diff'] = 'prev'; - $query['oldid'] = $this->mOldid; - # Cascade unhide param in links for easy deletion browsing - if ( $this->unhide ) { - $query['unhide'] = 1; - } - if ( !$this->mOldRev->getPrevious() ) { - $prevlink = ' '; - } else { - $prevlink = $sk->link( - $this->mTitle, - wfMsgHtml( 'previousdiff' ), - array( - 'id' => 'differences-prevlink' - ), - $query, - array( - 'known', - 'noclasses' - ) - ); + $ldel = $this->revisionDeleteLink( $this->mOldRev ); + $oldRevisionHeader = $this->getRevisionHeader( $this->mOldRev, 'complete' ); + + $oldHeader = '
' . $oldRevisionHeader . '
' . + '
' . + Linker::revUserTools( $this->mOldRev, !$this->unhide ) . '
' . + '
' . $oldminor . + Linker::revComment( $this->mOldRev, !$diffOnly, !$this->unhide ) . $ldel . '
' . + '
' . $prevlink . '
'; + + if ( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) { + $deleted = true; // old revisions text is hidden + if ( $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) ) { + $suppressed = true; // also suppressed + } + } + + # Check if this user can see the revisions + if ( !$this->mOldRev->userCan( Revision::DELETED_TEXT, $user ) ) { + $allowed = false; + } } # Make "next revision link" - $query['diff'] = 'next'; - $query['oldid'] = $this->mNewid; # Skip next link on the top revision - if ( $this->mNewRev->isCurrent() ) { - $nextlink = ' '; - } else { - $nextlink = $sk->link( - $this->mTitle, - wfMsgHtml( 'nextdiff' ), - array( - 'id' => 'differences-nextlink' - ), - $query, - array( - 'known', - 'noclasses' - ) + if ( $samePage && !$this->mNewRev->isCurrent() ) { + $nextlink = Linker::linkKnown( + $this->mNewPage, + $this->msg( 'nextdiff' )->escaped(), + array( 'id' => 'differences-nextlink' ), + array( 'diff' => 'next', 'oldid' => $this->mNewid ) + $query ); + } else { + $nextlink = ' '; } - $oldminor = ''; - $newminor = ''; - - if ( $this->mOldRev->isMinor() ) { - $oldminor = ChangesList::flag( 'minor' ); - } if ( $this->mNewRev->isMinor() ) { $newminor = ChangesList::flag( 'minor' ); + } else { + $newminor = ''; } # Handle RevisionDelete links... - $ldel = $this->revisionDeleteLink( $this->mOldRev ); $rdel = $this->revisionDeleteLink( $this->mNewRev ); + $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) . $undoLink; - $oldHeader = '
' . $this->mOldtitle . '
' . - '
' . - $sk->revUserTools( $this->mOldRev, !$this->unhide ) . '
' . - '
' . $oldminor . - $sk->revComment( $this->mOldRev, !$diffOnly, !$this->unhide ) . $ldel . '
' . - '
' . $prevlink . '
'; - $newHeader = '
' . $this->mNewtitle . '
' . - '
' . $sk->revUserTools( $this->mNewRev, !$this->unhide ) . + $newHeader = '
' . $newRevisionHeader . '
' . + '
' . Linker::revUserTools( $this->mNewRev, !$this->unhide ) . " $rollback
" . '
' . $newminor . - $sk->revComment( $this->mNewRev, !$diffOnly, !$this->unhide ) . $rdel . '
' . - '
' . $nextlink . $patrol . '
'; + Linker::revComment( $this->mNewRev, !$diffOnly, !$this->unhide ) . $rdel . '
' . + '
' . $nextlink . $this->markPatrolledLink() . '
'; - # Check if this user can see the revisions - $allowed = $this->mOldRev->userCan( Revision::DELETED_TEXT ) - && $this->mNewRev->userCan( Revision::DELETED_TEXT ); - # Check if one of the revisions is deleted/suppressed - $deleted = $suppressed = false; - if ( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) { - $deleted = true; // old revisions text is hidden - if ( $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) ) - $suppressed = true; // also suppressed - } if ( $this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) { $deleted = true; // new revisions text is hidden if ( $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) ) $suppressed = true; // also suppressed } + # If the diff cannot be shown due to a deleted revision, then output # the diff header and links to unhide (if available)... if ( $deleted && ( !$this->unhide || !$allowed ) ) { $this->showDiffStyle(); $multi = $this->getMultiNotice(); - $wgOut->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) ); + $out->addHTML( $this->addHeader( '', $oldHeader, $newHeader, $multi ) ); if ( !$allowed ) { $msg = $suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff'; # Give explanation for why revision is not visible - $wgOut->wrapWikiMsg( "\n", + $out->wrapWikiMsg( "\n", array( $msg ) ); } else { # Give explanation and add a link to view the diff... - $link = $this->mTitle->getFullUrl( array( - 'diff' => $this->mNewid, - 'oldid' => $this->mOldid, - 'unhide' => 1 - ) ); + $link = $this->getTitle()->getFullUrl( $this->getRequest()->appendQueryValue( 'unhide', '1', true ) ); $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; - $wgOut->wrapWikiMsg( "\n", array( $msg, $link ) ); + $out->wrapWikiMsg( "\n", array( $msg, $link ) ); } # Otherwise, output a regular diff... } else { @@ -458,7 +380,7 @@ CONTROL; $notice = ''; if ( $deleted ) { $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; - $notice = "\n"; + $notice = "\n"; } $this->showDiff( $oldHeader, $newHeader, $notice ); if ( !$diffOnly ) { @@ -469,29 +391,79 @@ CONTROL; } /** - * @param $rev Revision + * Get a link to mark the change as patrolled, or '' if there's either no + * revision to patrol or the user is not allowed to to it. + * Side effect: this method will call OutputPage::preventClickjacking() + * when a link is builded. + * * @return String */ - protected function revisionDeleteLink( $rev ) { - global $wgUser; - $link = ''; - $canHide = $wgUser->isAllowed( 'deleterevision' ); - // Show del/undel link if: - // (a) the user can delete revisions, or - // (b) the user can view deleted revision *and* this one is deleted - if ( $canHide || ( $rev->getVisibility() && $wgUser->isAllowed( 'deletedhistory' ) ) ) { - $sk = $wgUser->getSkin(); - if ( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) { - $link = $sk->revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops + protected function markPatrolledLink() { + global $wgUseRCPatrol; + + if ( $this->mMarkPatrolledLink === null ) { + // Prepare a change patrol link, if applicable + if ( $wgUseRCPatrol && $this->mNewPage->userCan( 'patrol', $this->getUser() ) ) { + // If we've been given an explicit change identifier, use it; saves time + if ( $this->mRcidMarkPatrolled ) { + $rcid = $this->mRcidMarkPatrolled; + $rc = RecentChange::newFromId( $rcid ); + // Already patrolled? + $rcid = is_object( $rc ) && !$rc->getAttribute( 'rc_patrolled' ) ? $rcid : 0; + } else { + // Look for an unpatrolled change corresponding to this diff + $db = wfGetDB( DB_SLAVE ); + $change = RecentChange::newFromConds( + array( + // Redundant user,timestamp condition so we can use the existing index + 'rc_user_text' => $this->mNewRev->getRawUserText(), + 'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ), + 'rc_this_oldid' => $this->mNewid, + 'rc_last_oldid' => $this->mOldid, + 'rc_patrolled' => 0 + ), + __METHOD__ + ); + if ( $change instanceof RecentChange ) { + $rcid = $change->mAttribs['rc_id']; + $this->mRcidMarkPatrolled = $rcid; + } else { + // None found + $rcid = 0; + } + } + // Build the link + if ( $rcid ) { + $this->getOutput()->preventClickjacking(); + $token = $this->getUser()->getEditToken( $rcid ); + $this->mMarkPatrolledLink = ' [' . Linker::linkKnown( + $this->mNewPage, + $this->msg( 'markaspatrolleddiff' )->escaped(), + array(), + array( + 'action' => 'markpatrolled', + 'rcid' => $rcid, + 'token' => $token, + ) + ) . ']'; + } else { + $this->mMarkPatrolledLink = ''; + } } else { - $query = array( - 'type' => 'revision', - 'target' => $rev->mTitle->getPrefixedDbkey(), - 'ids' => $rev->getId() - ); - $link = $sk->revDeleteLink( $query, - $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide ); + $this->mMarkPatrolledLink = ''; } + } + + return $this->mMarkPatrolledLink; + } + + /** + * @param $rev Revision + * @return String + */ + protected function revisionDeleteLink( $rev ) { + $link = Linker::getRevDeleteLink( $this->getUser(), $rev, $rev->getTitle() ); + if ( $link !== '' ) { $link = '   ' . $link . ' '; } return $link; @@ -501,156 +473,81 @@ CONTROL; * Show the new revision of the page. */ function renderNewRevision() { - global $wgOut, $wgUser; wfProfileIn( __METHOD__ ); + $out = $this->getOutput(); + $revHeader = $this->getRevisionHeader( $this->mNewRev ); # Add "current version as of X" title - $wgOut->addHTML( "
-

{$this->mPagetitle}

\n" ); + $out->addHTML( "
+

{$revHeader}

\n" ); # Page content may be handled by a hooked call instead... - if ( wfRunHooks( 'ArticleContentOnDiff', array( $this, $wgOut ) ) ) { - # Use the current version parser cache if applicable - $pCache = true; - if ( !$this->mNewRev->isCurrent() ) { - $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection( false ); - $pCache = false; - } - + if ( wfRunHooks( 'ArticleContentOnDiff', array( $this, $out ) ) ) { $this->loadNewText(); - $wgOut->setRevisionId( $this->mNewRev->getId() ); + $out->setRevisionId( $this->mNewid ); + $out->setRevisionTimestamp( $this->mNewRev->getTimestamp() ); + $out->setArticleFlag( true ); - if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) { + if ( $this->mNewPage->isCssJsSubpage() || $this->mNewPage->isCssOrJsPage() ) { // Stolen from Article::view --AG 2007-10-11 // Give hooks a chance to customise the output // @TODO: standardize this crap into one function - if ( wfRunHooks( 'ShowRawCssJs', array( $this->mNewtext, $this->mTitle, $wgOut ) ) ) { + if ( wfRunHooks( 'ShowRawCssJs', array( $this->mNewtext, $this->mNewPage, $out ) ) ) { // Wrap the whole lot in a
 and don't parse
 					$m = array();
-					preg_match( '!\.(css|js)$!u', $this->mTitle->getText(), $m );
-					$wgOut->addHTML( "
\n" );
-					$wgOut->addHTML( htmlspecialchars( $this->mNewtext ) );
-					$wgOut->addHTML( "\n
\n" ); + preg_match( '!\.(css|js)$!u', $this->mNewPage->getText(), $m ); + $out->addHTML( "
\n" );
+					$out->addHTML( htmlspecialchars( $this->mNewtext ) );
+					$out->addHTML( "\n
\n" ); } - } elseif ( $pCache ) { - $article = new Article( $this->mTitle, 0 ); - $pOutput = ParserCache::singleton()->get( $article, $wgOut->parserOptions() ); - if( $pOutput ) { - $wgOut->addParserOutput( $pOutput ); + } elseif ( !wfRunHooks( 'ArticleViewCustom', array( $this->mNewtext, $this->mNewPage, $out ) ) ) { + // Handled by extension + } else { + // Normal page + if ( $this->getTitle()->equals( $this->mNewPage ) ) { + // If the Title stored in the context is the same as the one + // of the new revision, we can use its associated WikiPage + // object. + $wikiPage = $this->getWikiPage(); } else { - $article->doViewParse(); + // Otherwise we need to create our own WikiPage object + $wikiPage = WikiPage::factory( $this->mNewPage ); } - } else { - $wgOut->addWikiTextTidy( $this->mNewtext ); - } - if ( !$this->mNewRev->isCurrent() ) { - $wgOut->parserOptions()->setEditSection( $oldEditSectionSetting ); - } - } - # Add redundant patrol link on bottom... - if ( $this->mRcidMarkPatrolled && $this->mTitle->quickUserCan( 'patrol' ) ) { - $sk = $wgUser->getSkin(); - $token = $wgUser->editToken( $this->mRcidMarkPatrolled ); - $wgOut->preventClickjacking(); - $wgOut->addHTML( - "' - ); - } + $parserOptions = ParserOptions::newFromContext( $this->getContext() ); + $parserOptions->enableLimitReport(); + $parserOptions->setTidy( true ); - wfProfileOut( __METHOD__ ); - } - - /** - * Show the first revision of an article. Uses normal diff headers in - * contrast to normal "old revision" display style. - */ - function showFirstRevision() { - global $wgOut, $wgUser; - wfProfileIn( __METHOD__ ); - - # Get article text from the DB - # - if ( ! $this->loadNewText() ) { - $t = $this->mTitle->getPrefixedText(); - $d = wfMsgExt( 'missingarticle-diff', array( 'escape' ), - $this->deletedIdMarker( $this->mOldid ), - $this->deletedIdMarker( $this->mNewid ) ); - $wgOut->setPagetitle( wfMsg( 'errorpagetitle' ) ); - $wgOut->addWikiMsg( 'missing-article', "$t", "$d" ); - wfProfileOut( __METHOD__ ); - return; - } - if ( $this->mNewRev->isCurrent() ) { - $wgOut->setArticleFlag( true ); - } - - # Check if user is allowed to look at this page. If not, bail out. - # - if ( !$this->mTitle->userCanRead() ) { - $wgOut->loginToUse(); - $wgOut->output(); - wfProfileOut( __METHOD__ ); - throw new MWException( "Permission Error: you do not have access to view this page" ); - } + if ( !$this->mNewRev->isCurrent() ) { + $parserOptions->setEditSection( false ); + } - # Prepare the header box - # - $sk = $wgUser->getSkin(); + $parserOutput = $wikiPage->getParserOutput( $parserOptions, $this->mNewid ); - $next = $this->mTitle->getNextRevisionID( $this->mNewid ); - if ( !$next ) { - $nextlink = ''; - } else { - $nextlink = '
' . $sk->link( - $this->mTitle, - wfMsgHtml( 'nextdiff' ), - array( - 'id' => 'differences-nextlink' - ), - array( - 'diff' => 'next', - 'oldid' => $this->mNewid, - ), - array( - 'known', - 'noclasses' - ) - ); + # WikiPage::getParserOutput() should not return false, but just in case + if( $parserOutput ) { + $out->addParserOutput( $parserOutput ); + } + } } - $header = "
" . - $sk->revUserTools( $this->mNewRev ) . "
" . $sk->revComment( $this->mNewRev ) . $nextlink . "
\n"; - - $wgOut->addHTML( $header ); - - $wgOut->setSubtitle( wfMsgExt( 'difference', array( 'parseinline' ) ) ); - $wgOut->setRobotPolicy( 'noindex,nofollow' ); + # Add redundant patrol link on bottom... + $out->addHTML( $this->markPatrolledLink() ); wfProfileOut( __METHOD__ ); } /** - * Get the diff text, send it to $wgOut + * Get the diff text, send it to the OutputPage object * Returns false if the diff could not be generated, otherwise returns true * * @return bool */ function showDiff( $otitle, $ntitle, $notice = '' ) { - global $wgOut; $diff = $this->getDiff( $otitle, $ntitle, $notice ); if ( $diff === false ) { - $wgOut->addWikiMsg( 'missing-article', "(fixme, bug)", '' ); + $this->getOutput()->addWikiMsg( 'missing-article', "(fixme, bug)", '' ); return false; } else { $this->showDiffStyle(); - $wgOut->addHTML( $diff ); + $this->getOutput()->addHTML( $diff ); return true; } } @@ -659,8 +556,7 @@ CONTROL; * Add style sheets and supporting JS for diff display. */ function showDiffStyle() { - global $wgOut; - $wgOut->addModuleStyles( 'mediawiki.action.history.diff' ); + $this->getOutput()->addModuleStyles( 'mediawiki.action.history.diff' ); } /** @@ -694,16 +590,17 @@ CONTROL; if ( !$this->loadRevisionData() ) { wfProfileOut( __METHOD__ ); return false; - } elseif ( $this->mOldRev && !$this->mOldRev->userCan( Revision::DELETED_TEXT ) ) { + } elseif ( $this->mOldRev && !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { wfProfileOut( __METHOD__ ); return false; - } elseif ( $this->mNewRev && !$this->mNewRev->userCan( Revision::DELETED_TEXT ) ) { + } elseif ( $this->mNewRev && !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { wfProfileOut( __METHOD__ ); return false; } // Short-circuit - if ( $this->mOldRev && $this->mNewRev - && $this->mOldRev->getID() == $this->mNewRev->getID() ) + // If mOldRev is false, it means that the + if ( $this->mOldRev === false || ( $this->mOldRev && $this->mNewRev + && $this->mOldRev->getID() == $this->mNewRev->getID() ) ) { wfProfileOut( __METHOD__ ); return ''; @@ -878,9 +775,8 @@ CONTROL; } function localiseLineNumbersCb( $matches ) { - global $wgLang; if ( $matches[1] === '1' && $this->mReducedLineNumbers ) return ''; - return wfMsgExt( 'lineno', 'escape', $wgLang->formatNum( $matches[1] ) ); + return $this->msg( 'lineno' )->numParams( $matches[1] )->escaped(); } @@ -904,10 +800,10 @@ CONTROL; $newRev = $this->mNewRev; } - $nEdits = $this->mTitle->countRevisionsBetween( $oldRev, $newRev ); + $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev, $newRev ); if ( $nEdits > 0 ) { $limit = 100; // use diff-multi-manyusers if too many users - $numUsers = $this->mTitle->countAuthorsBetween( $oldRev, $newRev, $limit ); + $numUsers = $this->mNewPage->countAuthorsBetween( $oldRev, $newRev, $limit ); return self::intermediateEditsMsg( $nEdits, $numUsers, $limit ); } return ''; // nothing @@ -921,15 +817,63 @@ CONTROL; * @return string */ public static function intermediateEditsMsg( $numEdits, $numUsers, $limit ) { - global $wgLang; if ( $numUsers > $limit ) { $msg = 'diff-multi-manyusers'; $numUsers = $limit; } else { $msg = 'diff-multi'; } - return wfMsgExt( $msg, 'parseinline', - $wgLang->formatnum( $numEdits ), $wgLang->formatnum( $numUsers ) ); + return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse(); + } + + /** + * Get a header for a specified revision. + * + * @param $rev Revision + * @param $complete String: 'complete' to get the header wrapped depending + * the visibility of the revision and a link to edit the page. + * @return String HTML fragment + */ + private function getRevisionHeader( Revision $rev, $complete = '' ) { + $lang = $this->getLanguage(); + $user = $this->getUser(); + $revtimestamp = $rev->getTimestamp(); + $timestamp = $lang->userTimeAndDate( $revtimestamp, $user ); + $dateofrev = $lang->userDate( $revtimestamp, $user ); + $timeofrev = $lang->userTime( $revtimestamp, $user ); + + $header = $this->msg( + $rev->isCurrent() ? 'currentrev-asof' : 'revisionasof', + $timestamp, + $dateofrev, + $timeofrev + )->escaped(); + + if ( $complete !== 'complete' ) { + return $header; + } + + $title = $rev->getTitle(); + + $header = Linker::linkKnown( $title, $header, array(), + array( 'oldid' => $rev->getID() ) ); + + if ( $rev->userCan( Revision::DELETED_TEXT, $user ) ) { + $editQuery = array( 'action' => 'edit' ); + if ( !$rev->isCurrent() ) { + $editQuery['oldid'] = $rev->getID(); + } + + $msg = $this->msg( $title->userCan( 'edit', $user ) ? 'editold' : 'viewsourceold' )->escaped(); + $header .= ' (' . Linker::linkKnown( $title, $msg, array(), $editQuery ) . ')'; + if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { + $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header ); + } + } else { + $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header ); + } + + return $header; } /** @@ -938,28 +882,36 @@ CONTROL; * @return string */ function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) { - // shared.css sets diff in interface language/dir, - // but the actual content should be in the page language/dir - $pageLang = $this->mTitle->getPageLanguage(); - $tableClass = 'diff diff-contentalign-' . htmlspecialchars( $pageLang->alignStart() ); + // shared.css sets diff in interface language/dir, but the actual content + // is often in a different language, mostly the page content language/dir + $tableClass = 'diff diff-contentalign-' . htmlspecialchars( $this->getDiffLang()->alignStart() ); $header = ""; - if ( $diff ) { // Safari/Chrome show broken output if cols not used + + if ( !$diff && !$otitle ) { $header .= " - - - - "; - $colspan = 2; - $multiColspan = 4; + + + "; + $multiColspan = 1; } else { - $colspan = 1; - $multiColspan = 2; + if ( $diff ) { // Safari/Chrome show broken output if cols not used + $header .= " + + + + "; + $colspan = 2; + $multiColspan = 4; + } else { + $colspan = 1; + $multiColspan = 2; + } + $header .= " + + + + "; } - $header .= " - - - - "; if ( $multi != '' ) { $header .= ""; @@ -981,6 +933,50 @@ CONTROL; $this->mRevisionsLoaded = true; } + /** + * Set the language in which the diff text is written + * (Defaults to page content language). + * @since 1.19 + */ + function setTextLanguage( $lang ) { + $this->mDiffLang = wfGetLangObj( $lang ); + } + + /** + * Load revision IDs + */ + private function loadRevisionIds() { + if ( $this->mRevisionsIdsLoaded ) { + return; + } + + $this->mRevisionsIdsLoaded = true; + + $old = $this->mOldid; + $new = $this->mNewid; + + if ( $new === 'prev' ) { + # Show diff between revision $old and the previous one. + # Get previous one from DB. + $this->mNewid = intval( $old ); + $this->mOldid = $this->getTitle()->getPreviousRevisionID( $this->mNewid ); + } elseif ( $new === 'next' ) { + # Show diff between revision $old and the next one. + # Get next one from DB. + $this->mOldid = intval( $old ); + $this->mNewid = $this->getTitle()->getNextRevisionID( $this->mOldid ); + if ( $this->mNewid === false ) { + # if no result, NewId points to the newest old revision. The only newer + # revision is cur, which is "0". + $this->mNewid = 0; + } + } else { + $this->mOldid = intval( $old ); + $this->mNewid = intval( $new ); + wfRunHooks( 'NewDifferenceEngine', array( $this->getTitle(), &$this->mOldid, &$this->mNewid, $old, $new ) ); + } + } + /** * Load revision metadata for the specified articles. If newid is 0, then compare * the old article in oldid to the current article; if oldid is 0, then @@ -994,72 +990,27 @@ CONTROL; * @return bool */ function loadRevisionData() { - global $wgLang, $wgUser; if ( $this->mRevisionsLoaded ) { return true; - } else { - // Whether it succeeds or fails, we don't want to try again - $this->mRevisionsLoaded = true; } + // Whether it succeeds or fails, we don't want to try again + $this->mRevisionsLoaded = true; + + $this->loadRevisionIds(); + // Load the new revision object $this->mNewRev = $this->mNewid ? Revision::newFromId( $this->mNewid ) - : Revision::newFromTitle( $this->mTitle ); + : Revision::newFromTitle( $this->getTitle() ); + if ( !$this->mNewRev instanceof Revision ) { return false; } // Update the new revision ID in case it was 0 (makes life easier doing UI stuff) $this->mNewid = $this->mNewRev->getId(); - - // Check if page is editable - $editable = $this->mNewRev->getTitle()->userCan( 'edit' ); - - // Set assorted variables - $timestamp = $wgLang->timeanddate( $this->mNewRev->getTimestamp(), true ); - $dateofrev = $wgLang->date( $this->mNewRev->getTimestamp(), true ); - $timeofrev = $wgLang->time( $this->mNewRev->getTimestamp(), true ); $this->mNewPage = $this->mNewRev->getTitle(); - if ( $this->mNewRev->isCurrent() ) { - $newLink = $this->mNewPage->escapeLocalUrl( array( - 'oldid' => $this->mNewid - ) ); - $this->mPagetitle = htmlspecialchars( wfMsg( - 'currentrev-asof', - $timestamp, - $dateofrev, - $timeofrev - ) ); - $newEdit = $this->mNewPage->escapeLocalUrl( array( - 'action' => 'edit' - ) ); - - $this->mNewtitle = "{$this->mPagetitle}"; - $this->mNewtitle .= " (" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . ")"; - } else { - $newLink = $this->mNewPage->escapeLocalUrl( array( - 'oldid' => $this->mNewid - ) ); - $newEdit = $this->mNewPage->escapeLocalUrl( array( - 'action' => 'edit', - 'oldid' => $this->mNewid - ) ); - $this->mPagetitle = htmlspecialchars( wfMsg( - 'revisionasof', - $timestamp, - $dateofrev, - $timeofrev - ) ); - - $this->mNewtitle = "{$this->mPagetitle}"; - $this->mNewtitle .= " (" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . ")"; - } - if ( !$this->mNewRev->userCan( Revision::DELETED_TEXT ) ) { - $this->mNewtitle = "{$this->mPagetitle}"; - } elseif ( $this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) { - $this->mNewtitle = "{$this->mNewtitle}"; - } // Load the old revision object $this->mOldRev = false; @@ -1083,38 +1034,6 @@ CONTROL; if ( $this->mOldRev ) { $this->mOldPage = $this->mOldRev->getTitle(); - - $t = $wgLang->timeanddate( $this->mOldRev->getTimestamp(), true ); - $dateofrev = $wgLang->date( $this->mOldRev->getTimestamp(), true ); - $timeofrev = $wgLang->time( $this->mOldRev->getTimestamp(), true ); - $oldLink = $this->mOldPage->escapeLocalUrl( array( - 'oldid' => $this->mOldid - ) ); - $oldEdit = $this->mOldPage->escapeLocalUrl( array( - 'action' => 'edit', - 'oldid' => $this->mOldid - ) ); - $this->mOldPagetitle = htmlspecialchars( wfMsg( 'revisionasof', $t, $dateofrev, $timeofrev ) ); - - $this->mOldtitle = "{$this->mOldPagetitle}" - . " (" . wfMsgHtml( $editable ? 'editold' : 'viewsourceold' ) . ")"; - // Add an "undo" link - if ( $editable && !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) { - $undoLink = Html::element( 'a', array( - 'href' => $this->mNewPage->getLocalUrl( array( - 'action' => 'edit', - 'undoafter' => $this->mOldid, - 'undo' => $this->mNewid ) ), - 'title' => $wgUser->getSkin()->titleAttrib( 'undo' ) - ), wfMsg( 'editundo' ) ); - $this->mNewtitle .= ' (' . $undoLink . ')'; - } - - if ( !$this->mOldRev->userCan( Revision::DELETED_TEXT ) ) { - $this->mOldtitle = '' . $this->mOldPagetitle . ''; - } elseif ( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) { - $this->mOldtitle = '' . $this->mOldtitle . ''; - } } return true; diff --git a/includes/diff/WikiDiff3.php b/includes/diff/WikiDiff3.php index 27d3d5b8..66727445 100644 --- a/includes/diff/WikiDiff3.php +++ b/includes/diff/WikiDiff3.php @@ -546,9 +546,12 @@ class WikiDiff3 { } // return the middle diagonal with maximal progress. - return $max_progress[floor( $num_progress / 2 )]; + return $max_progress[(int)floor( $num_progress / 2 )]; } + /** + * @return mixed + */ public function getLcsLength() { if ( $this->heuristicUsed && !$this->lcsLengthCorrectedForHeuristic ) { $this->lcsLengthCorrectedForHeuristic = true; -- cgit v1.2.2
{$ntitle}
{$otitle}{$ntitle}
{$otitle}{$ntitle}
{$multi}