From c1f9b1f7b1b77776192048005dcc66dcf3df2bfb Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 27 Dec 2014 15:41:37 +0100 Subject: Update to MediaWiki 1.24.1 --- includes/diff/DifferenceEngine.php | 502 ++++++++++++++++++++++++------------- 1 file changed, 330 insertions(+), 172 deletions(-) (limited to 'includes/diff/DifferenceEngine.php') diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php index e436f58d..50e08ca1 100644 --- a/includes/diff/DifferenceEngine.php +++ b/includes/diff/DifferenceEngine.php @@ -34,60 +34,80 @@ define( 'MW_DIFF_VERSION', '1.11a' ); * @ingroup DifferenceEngine */ class DifferenceEngine extends ContextSource { - /**#@+ - * @private - */ - var $mOldid, $mNewid; - var $mOldTags, $mNewTags; - /** - * @var Content - */ - var $mOldContent, $mNewContent; + + /** @var int */ + public $mOldid; + + /** @var int */ + public $mNewid; + + private $mOldTags; + private $mNewTags; + + /** @var Content */ + public $mOldContent; + + /** @var Content */ + public $mNewContent; + + /** @var Language */ protected $mDiffLang; - /** - * @var Title - */ - var $mOldPage, $mNewPage; + /** @var Title */ + public $mOldPage; - /** - * @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? + /** @var Title */ + public $mNewPage; + + /** @var Revision */ + public $mOldRev; + + /** @var Revision */ + public $mNewRev; + + /** @var bool Have the revisions IDs been loaded */ + private $mRevisionsIdsLoaded = false; + + /** @var bool Have the revisions been loaded */ + public $mRevisionsLoaded = false; + + /** @var int How many text blobs have been loaded, 0, 1 or 2? */ + public $mTextLoaded = 0; + + /** @var bool Was the diff fetched from cache? */ + public $mCacheHit = false; /** * Set this to true to add debug info to the HTML output. * Warning: this may cause RSS readers to spuriously mark articles as "new" * (bug 20601) */ - var $enableDebugComment = false; + public $enableDebugComment = false; - // If true, line X is not displayed when X is 1, for example to increase - // readability and conserve space with many small diffs. + /** @var bool If true, line X is not displayed when X is 1, for example + * to increase readability and conserve space with many small diffs. + */ protected $mReducedLineNumbers = false; - // Link to action=markpatrolled + /** @var string Link to action=markpatrolled */ protected $mMarkPatrolledLink = null; - protected $unhide = false; # show rev_deleted content if allowed + /** @var bool Show rev_deleted content if allowed */ + protected $unhide = false; /**#@-*/ /** * Constructor - * @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 Deprecated, no longer used! - * @param $refreshCache boolean If set, refreshes the diff cache - * @param $unhide boolean If set, allow viewing deleted revs + * @param IContextSource $context Context to use, anything else will be ignored + * @param int $old Old ID we want to show and diff with. + * @param string|int $new Either revision ID or 'prev' or 'next'. Default: 0. + * @param int $rcid Deprecated, no longer used! + * @param bool $refreshCache If set, refreshes the diff cache + * @param bool $unhide If set, allow viewing deleted revs */ - function __construct( $context = null, $old = 0, $new = 0, $rcid = 0, - $refreshCache = false, $unhide = false ) - { + public function __construct( $context = null, $old = 0, $new = 0, $rcid = 0, + $refreshCache = false, $unhide = false + ) { if ( $context instanceof IContextSource ) { $this->setContext( $context ); } @@ -101,43 +121,46 @@ class DifferenceEngine extends ContextSource { } /** - * @param $value bool + * @param bool $value */ - function setReducedLineNumbers( $value = true ) { + public function setReducedLineNumbers( $value = true ) { $this->mReducedLineNumbers = $value; } /** * @return Language */ - function getDiffLang() { + public function getDiffLang() { if ( $this->mDiffLang === null ) { # Default language in which the diff text is written. $this->mDiffLang = $this->getTitle()->getPageLanguage(); } + return $this->mDiffLang; } /** * @return bool */ - function wasCacheHit() { + public function wasCacheHit() { return $this->mCacheHit; } /** * @return int */ - function getOldid() { + public function getOldid() { $this->loadRevisionIds(); + return $this->mOldid; } /** - * @return Bool|int + * @return bool|int */ - function getNewid() { + public function getNewid() { $this->loadRevisionIds(); + return $this->mNewid; } @@ -145,10 +168,11 @@ class DifferenceEngine extends ContextSource { * Look up a special:Undelete link to the given deleted revision id, * as a workaround for being unable to load deleted diffs in currently. * - * @param int $id revision ID + * @param int $id Revision ID + * * @return mixed URL or false */ - function deletedLink( $id ) { + public function deletedLink( $id ) { if ( $this->getUser()->isAllowed( 'deletedhistory' ) ) { $dbr = wfGetDB( DB_SLAVE ); $row = $dbr->selectRow( 'archive', '*', @@ -157,22 +181,25 @@ class DifferenceEngine extends ContextSource { if ( $row ) { $rev = Revision::newFromArchiveRow( $row ); $title = Title::makeTitleSafe( $row->ar_namespace, $row->ar_title ); + return SpecialPage::getTitleFor( 'Undelete' )->getFullURL( array( 'target' => $title->getPrefixedText(), 'timestamp' => $rev->getTimestamp() - )); + ) ); } } + return false; } /** * Build a wikitext link toward a deleted revision, if viewable. * - * @param int $id revision ID - * @return string wikitext fragment + * @param int $id Revision ID + * + * @return string Wikitext fragment */ - function deletedIdMarker( $id ) { + public function deletedIdMarker( $id ) { $link = $this->deletedLink( $id ); if ( $link ) { return "[$link $id]"; @@ -201,7 +228,7 @@ class DifferenceEngine extends ContextSource { $this->getLanguage()->listToText( $missing ), count( $missing ) ); } - function showDiffPage( $diffOnly = false ) { + public function showDiffPage( $diffOnly = false ) { wfProfileIn( __METHOD__ ); # Allow frames except in certain special cases @@ -212,6 +239,7 @@ class DifferenceEngine extends ContextSource { if ( !$this->loadRevisionData() ) { $this->showMissingRevision(); wfProfileOut( __METHOD__ ); + return; } @@ -227,7 +255,6 @@ class DifferenceEngine extends ContextSource { } $rollback = ''; - $undoLink = ''; $query = array(); # Carry over 'diffonly' param via navigation links @@ -259,8 +286,8 @@ class DifferenceEngine extends ContextSource { $out->setPageTitle( $this->msg( 'difference-title', $this->mNewPage->getPrefixedText() ) ); $samePage = true; } else { - $out->setPageTitle( $this->msg( 'difference-title-multipage', $this->mOldPage->getPrefixedText(), - $this->mNewPage->getPrefixedText() ) ); + $out->setPageTitle( $this->msg( 'difference-title-multipage', + $this->mOldPage->getPrefixedText(), $this->mNewPage->getPrefixedText() ) ); $out->addSubtitle( $this->msg( 'difference-multipage' ) ); $samePage = false; } @@ -273,17 +300,21 @@ class DifferenceEngine extends ContextSource { $rollback = '   ' . $rollbackLink; } } - if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) && !$this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) { + + if ( !$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' => Linker::titleAttrib( 'undo' ) + 'undo' => $this->mNewid + ) ), + 'title' => Linker::titleAttrib( 'undo' ), ), $this->msg( 'editundo' )->text() ); - $revisionTools[] = $undoLink; + $revisionTools['mw-diff-undo'] = $undoLink; } } @@ -311,9 +342,9 @@ class DifferenceEngine extends ContextSource { $oldHeader = '
' . $oldRevisionHeader . '
' . '
' . - Linker::revUserTools( $this->mOldRev, !$this->unhide ) . '
' . + Linker::revUserTools( $this->mOldRev, !$this->unhide ) . '' . '
' . $oldminor . - Linker::revComment( $this->mOldRev, !$diffOnly, !$this->unhide ) . $ldel . '
' . + Linker::revComment( $this->mOldRev, !$diffOnly, !$this->unhide ) . $ldel . '' . '
' . $oldChangeTags[0] . '
' . '
' . $prevlink . '
'; @@ -353,20 +384,27 @@ class DifferenceEngine extends ContextSource { $rdel = $this->revisionDeleteLink( $this->mNewRev ); # Allow extensions to define their own revision tools - wfRunHooks( 'DiffRevisionTools', array( $this->mNewRev, &$revisionTools ) ); + wfRunHooks( 'DiffRevisionTools', array( $this->mNewRev, &$revisionTools, $this->mOldRev ) ); $formattedRevisionTools = array(); // Put each one in parentheses (poor man's button) - foreach ( $revisionTools as $tool ) { - $formattedRevisionTools[] = $this->msg( 'parentheses' )->rawParams( $tool )->escaped(); + foreach ( $revisionTools as $key => $tool ) { + $toolClass = is_string( $key ) ? $key : 'mw-diff-tool'; + $element = Html::rawElement( + 'span', + array( 'class' => $toolClass ), + $this->msg( 'parentheses' )->rawParams( $tool )->escaped() + ); + $formattedRevisionTools[] = $element; } - $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) . ' ' . implode( ' ', $formattedRevisionTools ); + $newRevisionHeader = $this->getRevisionHeader( $this->mNewRev, 'complete' ) . + ' ' . implode( ' ', $formattedRevisionTools ); $newChangeTags = ChangeTags::formatSummaryRow( $this->mNewTags, 'diff' ); $newHeader = '
' . $newRevisionHeader . '
' . '
' . Linker::revUserTools( $this->mNewRev, !$this->unhide ) . - " $rollback
" . + " $rollback" . '
' . $newminor . - Linker::revComment( $this->mNewRev, !$diffOnly, !$this->unhide ) . $rdel . '
' . + Linker::revComment( $this->mNewRev, !$diffOnly, !$this->unhide ) . $rdel . '' . '
' . $newChangeTags[0] . '
' . '
' . $nextlink . $this->markPatrolledLink() . '
'; @@ -390,9 +428,13 @@ class DifferenceEngine extends ContextSource { array( $msg ) ); } else { # Give explanation and add a link to view the diff... - $link = $this->getTitle()->getFullURL( $this->getRequest()->appendQueryValue( 'unhide', '1', true ) ); + $query = $this->getRequest()->appendQueryValue( 'unhide', '1', true ); + $link = $this->getTitle()->getFullURL( $query ); $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff'; - $out->wrapWikiMsg( "\n", array( $msg, $link ) ); + $out->wrapWikiMsg( + "\n", + array( $msg, $link ) + ); } # Otherwise, output a regular diff... } else { @@ -400,7 +442,9 @@ class DifferenceEngine extends ContextSource { $notice = ''; if ( $deleted ) { $msg = $suppressed ? 'rev-suppressed-diff-view' : 'rev-deleted-diff-view'; - $notice = "\n"; + $notice = "\n"; } $this->showDiff( $oldHeader, $newHeader, $notice ); if ( !$diffOnly ) { @@ -416,7 +460,7 @@ class DifferenceEngine extends ContextSource { * Side effect: When the patrol link is build, this method will call * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax. * - * @return String + * @return string */ protected function markPatrolledLink() { global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI; @@ -483,21 +527,23 @@ class DifferenceEngine extends ContextSource { } /** - * @param $rev Revision - * @return String + * @param Revision $rev + * + * @return string */ protected function revisionDeleteLink( $rev ) { $link = Linker::getRevDeleteLink( $this->getUser(), $rev, $rev->getTitle() ); if ( $link !== '' ) { $link = '   ' . $link . ' '; } + return $link; } /** * Show the new revision of the page. */ - function renderNewRevision() { + public function renderNewRevision() { wfProfileIn( __METHOD__ ); $out = $this->getOutput(); $revHeader = $this->getRevisionHeader( $this->mNewRev ); @@ -505,6 +551,7 @@ class DifferenceEngine extends ContextSource { $out->addHTML( "

{$revHeader}

\n" ); # Page content may be handled by a hooked call instead... + # @codingStandardsIgnoreStart Ignoring long lines. if ( wfRunHooks( 'ArticleContentOnDiff', array( $this, $out ) ) ) { $this->loadNewText(); $out->setRevisionId( $this->mNewid ); @@ -513,7 +560,7 @@ class DifferenceEngine extends ContextSource { // NOTE: only needed for B/C: custom rendering of JS/CSS via hook if ( $this->mNewPage->isCssJsSubpage() || $this->mNewPage->isCssOrJsPage() ) { - // Stolen from Article::view --AG 2007-10-11 + // This needs to be synchronised with Article::showCssOrJsPage(), which sucks // Give hooks a chance to customise the output // @todo standardize this crap into one function if ( ContentHandler::runLegacyHooks( 'ShowRawCssJs', array( $this->mNewContent, $this->mNewPage, $out ) ) ) { @@ -521,8 +568,9 @@ class DifferenceEngine extends ContextSource { // use the content object's own rendering $cnt = $this->mNewRev->getContent(); $po = $cnt ? $cnt->getParserOutput( $this->mNewRev->getTitle(), $this->mNewRev->getId() ) : null; - $txt = $po ? $po->getText() : ''; - $out->addHTML( $txt ); + if ( $po ) { + $out->addParserOutputContent( $po ); + } } } elseif ( !wfRunHooks( 'ArticleContentViewCustom', array( $this->mNewContent, $this->mNewPage, $out ) ) ) { // Handled by extension @@ -543,23 +591,14 @@ class DifferenceEngine extends ContextSource { $parserOutput = $this->getParserOutput( $wikiPage, $this->mNewRev ); - # Also try to load it as a redirect - $rt = $this->mNewContent ? $this->mNewContent->getRedirectTarget() : null; - - if ( $rt ) { - $article = Article::newFromTitle( $this->mNewPage, $this->getContext() ); - $out->addHTML( $article->viewRedirect( $rt ) ); - - # WikiPage::getParserOutput() should not return false, but just in case - if ( $parserOutput ) { - # Show categories etc. - $out->addParserOutputNoText( $parserOutput ); - } - } elseif ( $parserOutput ) { + # WikiPage::getParserOutput() should not return false, but just in case + if ( $parserOutput ) { $out->addParserOutput( $parserOutput ); } } } + # @codingStandardsIgnoreEnd + # Add redundant patrol link on bottom... $out->addHTML( $this->markPatrolledLink() ); @@ -574,6 +613,7 @@ class DifferenceEngine extends ContextSource { } $parserOutput = $page->getParserOutput( $parserOptions, $rev->getId() ); + return $parserOutput; } @@ -581,16 +621,22 @@ class DifferenceEngine extends ContextSource { * Get the diff text, send it to the OutputPage object * Returns false if the diff could not be generated, otherwise returns true * + * @param string|bool $otitle Header for old text or false + * @param string|bool $ntitle Header for new text or false + * @param string $notice HTML between diff header and body + * * @return bool */ - function showDiff( $otitle, $ntitle, $notice = '' ) { + public function showDiff( $otitle, $ntitle, $notice = '' ) { $diff = $this->getDiff( $otitle, $ntitle, $notice ); if ( $diff === false ) { $this->showMissingRevision(); + return false; } else { $this->showDiffStyle(); $this->getOutput()->addHTML( $diff ); + return true; } } @@ -598,7 +644,7 @@ class DifferenceEngine extends ContextSource { /** * Add style sheets and supporting JS for diff display. */ - function showDiffStyle() { + public function showDiffStyle() { $this->getOutput()->addModuleStyles( 'mediawiki.action.history.diff' ); } @@ -608,20 +654,24 @@ class DifferenceEngine extends ContextSource { * @param string|bool $otitle Header for old text or false * @param string|bool $ntitle Header for new text or false * @param string $notice HTML between diff header and body + * * @return mixed */ - function getDiff( $otitle, $ntitle, $notice = '' ) { + public function getDiff( $otitle, $ntitle, $notice = '' ) { $body = $this->getDiffBody(); if ( $body === false ) { return false; - } else { - $multi = $this->getMultiNotice(); - // Display a message when the diff is empty - if ( $body === '' ) { - $notice .= '
' . $this->msg( 'diff-empty' )->parse() . "
\n"; - } - return $this->addHeader( $body, $otitle, $ntitle, $multi, $notice ); } + + $multi = $this->getMultiNotice(); + // Display a message when the diff is empty + if ( $body === '' ) { + $notice .= '
' . + $this->msg( 'diff-empty' )->parse() . + "
\n"; + } + + return $this->addHeader( $body, $otitle, $ntitle, $multi, $notice ); } /** @@ -636,26 +686,34 @@ class DifferenceEngine extends ContextSource { // Check if the diff should be hidden from this user if ( !$this->loadRevisionData() ) { wfProfileOut( __METHOD__ ); + return false; - } elseif ( $this->mOldRev && !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { + } elseif ( $this->mOldRev && + !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) + ) { wfProfileOut( __METHOD__ ); + return false; - } elseif ( $this->mNewRev && !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) { + } elseif ( $this->mNewRev && + !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() ) + ) { wfProfileOut( __METHOD__ ); + return false; } // Short-circuit if ( $this->mOldRev === false || ( $this->mOldRev && $this->mNewRev - && $this->mOldRev->getID() == $this->mNewRev->getID() ) ) - { + && $this->mOldRev->getID() == $this->mNewRev->getID() ) + ) { wfProfileOut( __METHOD__ ); + return ''; } // Cacheable? $key = false; if ( $this->mOldid && $this->mNewid ) { - $key = wfMemcKey( 'diff', 'version', MW_DIFF_VERSION, - 'oldid', $this->mOldid, 'newid', $this->mNewid ); + $key = $this->getDiffBodyCacheKey(); + // Try cache if ( !$this->mRefreshCache ) { $difftext = $wgMemc->get( $key ); @@ -664,6 +722,7 @@ class DifferenceEngine extends ContextSource { $difftext = $this->localiseLineNumbers( $difftext ); $difftext .= "\n\n"; wfProfileOut( __METHOD__ ); + return $difftext; } } // don't try to load but save the result @@ -673,6 +732,7 @@ class DifferenceEngine extends ContextSource { // Loadtext is permission safe, this just clears out the diff if ( !$this->loadText() ) { wfProfileOut( __METHOD__ ); + return false; } @@ -692,9 +752,27 @@ class DifferenceEngine extends ContextSource { $difftext = $this->localiseLineNumbers( $difftext ); } wfProfileOut( __METHOD__ ); + return $difftext; } + /** + * Returns the cache key for diff body text or content. + * + * @since 1.23 + * + * @throws MWException + * @return string + */ + protected function getDiffBodyCacheKey() { + if ( !$this->mOldid || !$this->mNewid ) { + throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' ); + } + + return wfMemcKey( 'diff', 'version', MW_DIFF_VERSION, + 'oldid', $this->mOldid, 'newid', $this->mNewid ); + } + /** * Generate a diff, no caching. * @@ -706,17 +784,18 @@ class DifferenceEngine extends ContextSource { * perhaps taking advantage of the content's native form. This is required for all content * models that are not text based. * - * @param $old Content: old content - * @param $new Content: new content + * @since 1.21 + * + * @param Content $old Old content + * @param Content $new New content * + * @throws MWException If old or new content is not an instance of TextContent. * @return bool|string - * @since 1.21 - * @throws MWException if $old or $new are not instances of TextContent. */ - function generateContentDiffBody( Content $old, Content $new ) { + public function generateContentDiffBody( Content $old, Content $new ) { if ( !( $old instanceof TextContent ) ) { - throw new MWException( "Diff not implemented for " . get_class( $old ) . "; " - . "override generateContentDiffBody to fix this." ); + throw new MWException( "Diff not implemented for " . get_class( $old ) . "; " . + "override generateContentDiffBody to fix this." ); } if ( !( $new instanceof TextContent ) ) { @@ -733,12 +812,13 @@ class DifferenceEngine extends ContextSource { /** * Generate a diff, no caching * - * @param string $otext old text, must be already segmented - * @param string $ntext new text, must be already segmented + * @param string $otext Old text, must be already segmented + * @param string $ntext New text, must be already segmented + * * @return bool|string * @deprecated since 1.21, use generateContentDiffBody() instead! */ - function generateDiffBody( $otext, $ntext ) { + public function generateDiffBody( $otext, $ntext ) { ContentHandler::deprecated( __METHOD__, "1.21" ); return $this->generateTextDiffBody( $otext, $ntext ); @@ -749,11 +829,12 @@ class DifferenceEngine extends ContextSource { * * @todo move this to TextDifferenceEngine, make DifferenceEngine abstract. At some point. * - * @param string $otext old text, must be already segmented - * @param string $ntext new text, must be already segmented + * @param string $otext Old text, must be already segmented + * @param string $ntext New text, must be already segmented + * * @return bool|string */ - function generateTextDiffBody( $otext, $ntext ) { + public function generateTextDiffBody( $otext, $ntext ) { global $wgExternalDiffEngine, $wgContLang; wfProfileIn( __METHOD__ ); @@ -764,9 +845,10 @@ class DifferenceEngine extends ContextSource { if ( $wgExternalDiffEngine == 'wikidiff' && function_exists( 'wikidiff_do_diff' ) ) { # For historical reasons, external diff engine expects # input text to be HTML-escaped already - $otext = htmlspecialchars ( $wgContLang->segmentForDiff( $otext ) ); - $ntext = htmlspecialchars ( $wgContLang->segmentForDiff( $ntext ) ); + $otext = htmlspecialchars( $wgContLang->segmentForDiff( $otext ) ); + $ntext = htmlspecialchars( $wgContLang->segmentForDiff( $ntext ) ); wfProfileOut( __METHOD__ ); + return $wgContLang->unsegmentForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) ) . $this->debug( 'wikidiff1' ); } @@ -779,6 +861,7 @@ class DifferenceEngine extends ContextSource { $text .= $this->debug( 'wikidiff2' ); wfProfileOut( 'wikidiff2_do_diff' ); wfProfileOut( __METHOD__ ); + return $text; } if ( $wgExternalDiffEngine != 'wikidiff3' && $wgExternalDiffEngine !== false ) { @@ -790,11 +873,13 @@ class DifferenceEngine extends ContextSource { $tempFile1 = fopen( $tempName1, "w" ); if ( !$tempFile1 ) { wfProfileOut( __METHOD__ ); + return false; } $tempFile2 = fopen( $tempName2, "w" ); if ( !$tempFile2 ) { wfProfileOut( __METHOD__ ); + return false; } fwrite( $tempFile1, $otext ); @@ -809,6 +894,7 @@ class DifferenceEngine extends ContextSource { unlink( $tempName1 ); unlink( $tempName2 ); wfProfileOut( __METHOD__ ); + return $difftext; } @@ -818,13 +904,17 @@ class DifferenceEngine extends ContextSource { $diffs = new Diff( $ota, $nta ); $formatter = new TableDiffFormatter(); $difftext = $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) ) . - wfProfileOut( __METHOD__ ); + wfProfileOut( __METHOD__ ); + return $difftext; } /** * Generate a debug comment indicating diff generating time, * server node, and generator backend. + * + * @param string $generator : What diff engine was used + * * @return string */ protected function debug( $generator = "internal" ) { @@ -837,35 +927,41 @@ class DifferenceEngine extends ContextSource { $data[] = wfHostname(); } $data[] = wfTimestamp( TS_DB ); + return "\n"; } /** * Replace line numbers with the text in the user's language + * + * @param string $text + * * @return mixed */ - function localiseLineNumbers( $text ) { - return preg_replace_callback( '//', - array( &$this, 'localiseLineNumbersCb' ), $text ); + public function localiseLineNumbers( $text ) { + return preg_replace_callback( + '//', + array( &$this, 'localiseLineNumbersCb' ), + $text + ); } - function localiseLineNumbersCb( $matches ) { + public function localiseLineNumbersCb( $matches ) { if ( $matches[1] === '1' && $this->mReducedLineNumbers ) { return ''; } + return $this->msg( 'lineno' )->numParams( $matches[1] )->escaped(); } /** * If there are revisions between the ones being compared, return a note saying so. + * * @return string */ - function getMultiNotice() { + public function getMultiNotice() { if ( !is_object( $this->mOldRev ) || !is_object( $this->mNewRev ) ) { return ''; } elseif ( !$this->mOldPage->equals( $this->mNewPage ) ) { @@ -881,39 +977,54 @@ class DifferenceEngine extends ContextSource { $newRev = $this->mNewRev; } - $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev, $newRev ); - if ( $nEdits > 0 ) { + // Sanity: don't show the notice if too many rows must be scanned + // @todo show some special message for that case + $nEdits = $this->mNewPage->countRevisionsBetween( $oldRev, $newRev, 1000 ); + if ( $nEdits > 0 && $nEdits <= 1000 ) { $limit = 100; // use diff-multi-manyusers if too many users - $numUsers = $this->mNewPage->countAuthorsBetween( $oldRev, $newRev, $limit ); + $users = $this->mNewPage->getAuthorsBetween( $oldRev, $newRev, $limit ); + $numUsers = count( $users ); + + if ( $numUsers == 1 && $users[0] == $newRev->getRawUserText() ) { + $numUsers = 0; // special case to say "by the same user" instead of "by one other user" + } + return self::intermediateEditsMsg( $nEdits, $numUsers, $limit ); } + return ''; // nothing } /** * Get a notice about how many intermediate edits and users there are - * @param $numEdits int - * @param $numUsers int - * @param $limit int + * + * @param int $numEdits + * @param int $numUsers + * @param int $limit + * * @return string */ public static function intermediateEditsMsg( $numEdits, $numUsers, $limit ) { - if ( $numUsers > $limit ) { + if ( $numUsers === 0 ) { + $msg = 'diff-multi-sameuser'; + } elseif ( $numUsers > $limit ) { $msg = 'diff-multi-manyusers'; $numUsers = $limit; } else { - $msg = 'diff-multi'; + $msg = 'diff-multi-otherusers'; } + return wfMessage( $msg )->numParams( $numEdits, $numUsers )->parse(); } /** * Get a header for a specified revision. * - * @param $rev Revision + * @param Revision $rev * @param string $complete 'complete' to get the header wrapped depending * the visibility of the revision and a link to edit the page. - * @return String HTML fragment + * + * @return string HTML fragment */ protected function getRevisionHeader( Revision $rev, $complete = '' ) { $lang = $this->getLanguage(); @@ -945,11 +1056,21 @@ class DifferenceEngine extends ContextSource { $editQuery['oldid'] = $rev->getID(); } - $msg = $this->msg( $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold' )->escaped(); - $header .= ' ' . $this->msg( 'parentheses' )->rawParams( - Linker::linkKnown( $title, $msg, array(), $editQuery ) )->plain(); + $key = $title->quickUserCan( 'edit', $user ) ? 'editold' : 'viewsourceold'; + $msg = $this->msg( $key )->escaped(); + $editLink = $this->msg( 'parentheses' )->rawParams( + Linker::linkKnown( $title, $msg, array( ), $editQuery ) )->plain(); + $header .= ' ' . Html::rawElement( + 'span', + array( 'class' => 'mw-diff-edit' ), + $editLink + ); if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { - $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header ); + $header = Html::rawElement( + 'span', + array( 'class' => 'history-deleted' ), + $header + ); } } else { $header = Html::rawElement( 'span', array( 'class' => 'history-deleted' ), $header ); @@ -961,9 +1082,16 @@ class DifferenceEngine extends ContextSource { /** * Add the header to a diff body * + * @param string $diff Diff body + * @param string $otitle Old revision header + * @param string $ntitle New revision header + * @param string $multi Notice telling user that there are intermediate + * revisions between the ones being compared + * @param string $notice Other notices, e.g. that user is viewing deleted content + * * @return string */ - function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) { + public function addHeader( $diff, $otitle, $ntitle, $multi = '', $notice = '' ) { // 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() ); @@ -998,7 +1126,8 @@ class DifferenceEngine extends ContextSource { } if ( $multi != '' ) { - $header .= "{$multi}"; + $header .= "{$multi}"; } if ( $notice != '' ) { $header .= "{$notice}"; @@ -1011,7 +1140,7 @@ class DifferenceEngine extends ContextSource { * Use specified text instead of loading from the database * @deprecated since 1.21, use setContent() instead. */ - function setText( $oldText, $newText ) { + public function setText( $oldText, $newText ) { ContentHandler::deprecated( __METHOD__, "1.21" ); $oldContent = ContentHandler::makeContent( $oldText, $this->getTitle() ); @@ -1022,9 +1151,11 @@ class DifferenceEngine extends ContextSource { /** * Use specified text instead of loading from the database + * @param Content $oldContent + * @param Content $newContent * @since 1.21 */ - function setContent( Content $oldContent, Content $newContent ) { + public function setContent( Content $oldContent, Content $newContent ) { $this->mOldContent = $oldContent; $this->mNewContent = $newContent; @@ -1035,12 +1166,41 @@ class DifferenceEngine extends ContextSource { /** * Set the language in which the diff text is written * (Defaults to page content language). + * @param Language|string $lang * @since 1.19 */ - function setTextLanguage( $lang ) { + public function setTextLanguage( $lang ) { $this->mDiffLang = wfGetLangObj( $lang ); } + /** + * Maps a revision pair definition as accepted by DifferenceEngine constructor + * to a pair of actual integers representing revision ids. + * + * @param int $old Revision id, e.g. from URL parameter 'oldid' + * @param int|string $new Revision id or strings 'next' or 'prev', e.g. from URL parameter 'diff' + * + * @return int[] List of two revision ids, older first, later second. + * Zero signifies invalid argument passed. + * false signifies that there is no previous/next revision ($old is the oldest/newest one). + */ + public function mapDiffPrevNext( $old, $new ) { + if ( $new === 'prev' ) { + // Show diff between revision $old and the previous one. Get previous one from DB. + $newid = intval( $old ); + $oldid = $this->getTitle()->getPreviousRevisionID( $newid ); + } elseif ( $new === 'next' ) { + // Show diff between revision $old and the next one. Get next one from DB. + $oldid = intval( $old ); + $newid = $this->getTitle()->getNextRevisionID( $oldid ); + } else { + $oldid = intval( $old ); + $newid = intval( $new ); + } + + return array( $oldid, $newid ); + } + /** * Load revision IDs */ @@ -1054,26 +1214,17 @@ class DifferenceEngine extends ContextSource { $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 ) ); + list( $this->mOldid, $this->mNewid ) = self::mapDiffPrevNext( $old, $new ); + if ( $new === 'next' && $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; } + + wfRunHooks( + 'NewDifferenceEngine', + array( $this->getTitle(), &$this->mOldid, &$this->mNewid, $old, $new ) + ); } /** @@ -1088,7 +1239,7 @@ class DifferenceEngine extends ContextSource { * * @return bool */ - function loadRevisionData() { + public function loadRevisionData() { if ( $this->mRevisionsLoaded ) { return true; } @@ -1099,9 +1250,15 @@ class DifferenceEngine extends ContextSource { $this->loadRevisionIds(); // Load the new revision object - $this->mNewRev = $this->mNewid - ? Revision::newFromId( $this->mNewid ) - : Revision::newFromTitle( $this->getTitle(), false, Revision::READ_NORMAL ); + if ( $this->mNewid ) { + $this->mNewRev = Revision::newFromId( $this->mNewid ); + } else { + $this->mNewRev = Revision::newFromTitle( + $this->getTitle(), + false, + Revision::READ_NORMAL + ); + } if ( !$this->mNewRev instanceof Revision ) { return false; @@ -1162,7 +1319,7 @@ class DifferenceEngine extends ContextSource { * * @return bool */ - function loadText() { + public function loadText() { if ( $this->mTextLoaded == 2 ) { return true; } @@ -1196,7 +1353,7 @@ class DifferenceEngine extends ContextSource { * * @return bool */ - function loadNewText() { + public function loadNewText() { if ( $this->mTextLoaded >= 1 ) { return true; } @@ -1211,4 +1368,5 @@ class DifferenceEngine extends ContextSource { return true; } + } -- cgit v1.2.2