use MediaWiki\MediaWikiServices;
use MediaWiki\Shell\Shell;
-/** @deprecated use class constant instead */
-define( 'MW_DIFF_VERSION', '1.11a' );
-
/**
* @todo document
* @ingroup DifferenceEngine
* fixes important bugs or such to force cached diff views to
* clear.
*/
- const DIFF_VERSION = MW_DIFF_VERSION;
+ const DIFF_VERSION = '1.12';
/** @var int */
public $mOldid;
if ( Hooks::run( 'DifferenceEngineRenderRevisionAddParserOutput',
[ $this, $out, $parserOutput, $wikiPage ] )
) {
- $out->addParserOutput( $parserOutput );
+ $out->addParserOutput( $parserOutput, [
+ 'enableSectionEditLinks' => $this->mNewRev->isCurrent()
+ && $this->mNewRev->getTitle()->quickUserCan( 'edit', $this->getUser() ),
+ ] );
}
}
}
}
}
+ /**
+ * @param WikiPage $page
+ * @param Revision $rev
+ *
+ * @return ParserOutput|bool False if the revision was not found
+ */
protected function getParserOutput( WikiPage $page, Revision $rev ) {
$parserOptions = $page->makeParserOptions( $this->getContext() );
$key = false;
$cache = ObjectCache::getMainWANInstance();
if ( $this->mOldid && $this->mNewid ) {
+ // Check if subclass is still using the old way
+ // for backwards-compatibility
$key = $this->getDiffBodyCacheKey();
+ if ( $key === null ) {
+ $key = call_user_func_array(
+ [ $cache, 'makeKey' ],
+ $this->getDiffBodyCacheKeyParams()
+ );
+ }
// Try cache
if ( !$this->mRefreshCache ) {
$difftext = $cache->get( $key );
if ( $difftext ) {
wfIncrStats( 'diff_cache.hit' );
- $difftext = $this->localiseLineNumbers( $difftext );
+ $difftext = $this->localiseDiff( $difftext );
$difftext .= "\n<!-- diff cache key $key -->\n";
return $difftext;
} else {
wfIncrStats( 'diff_cache.uncacheable' );
}
- // Replace line numbers with the text in the user's language
+ // localise line numbers and title attribute text
if ( $difftext !== false ) {
- $difftext = $this->localiseLineNumbers( $difftext );
+ $difftext = $this->localiseDiff( $difftext );
}
return $difftext;
/**
* Returns the cache key for diff body text or content.
*
+ * @deprecated since 1.31, use getDiffBodyCacheKeyParams() instead
* @since 1.23
*
* @throws MWException
- * @return string
+ * @return string|null
*/
protected function getDiffBodyCacheKey() {
+ return null;
+ }
+
+ /**
+ * Get the cache key parameters
+ *
+ * Subclasses can replace the first element in the array to something
+ * more specific to the type of diff (e.g. "inline-diff"), or append
+ * if the cache should vary on more things. Overriding entirely should
+ * be avoided.
+ *
+ * @since 1.31
+ *
+ * @return array
+ * @throws MWException
+ */
+ protected function getDiffBodyCacheKeyParams() {
if ( !$this->mOldid || !$this->mNewid ) {
throw new MWException( 'mOldid and mNewid must be set to get diff cache key.' );
}
- return wfMemcKey( 'diff', 'version', self::DIFF_VERSION,
- 'oldid', $this->mOldid, 'newid', $this->mNewid );
+ $engine = $this->getEngine();
+ $params = [
+ 'diff',
+ $engine,
+ self::DIFF_VERSION,
+ "old-{$this->mOldid}",
+ "rev-{$this->mNewid}"
+ ];
+
+ if ( $engine === 'wikidiff2' ) {
+ $params[] = phpversion( 'wikidiff2' );
+ $params[] = $this->getConfig()->get( 'WikiDiff2MovedParagraphDetectionCutoff' );
+ }
+
+ return $params;
}
/**
}
/**
- * Generates diff, to be wrapped internally in a logging/instrumentation
+ * Process $wgExternalDiffEngine and get a sane, usable engine
*
- * @param string $otext Old text, must be already segmented
- * @param string $ntext New text, must be already segmented
- * @return bool|string
- * @throws Exception
+ * @return bool|string 'wikidiff2', path to an executable, or false
*/
- protected function textDiff( $otext, $ntext ) {
- global $wgExternalDiffEngine, $wgContLang;
-
- $otext = str_replace( "\r\n", "\n", $otext );
- $ntext = str_replace( "\r\n", "\n", $ntext );
-
+ private function getEngine() {
+ global $wgExternalDiffEngine;
+ // We use the global here instead of Config because we write to the value,
+ // and Config is not mutable.
if ( $wgExternalDiffEngine == 'wikidiff' || $wgExternalDiffEngine == 'wikidiff3' ) {
wfDeprecated( "\$wgExternalDiffEngine = '{$wgExternalDiffEngine}'", '1.27' );
$wgExternalDiffEngine = false;
$wgExternalDiffEngine = false;
}
+ if ( is_string( $wgExternalDiffEngine ) && is_executable( $wgExternalDiffEngine ) ) {
+ return $wgExternalDiffEngine;
+ } elseif ( $wgExternalDiffEngine === false && function_exists( 'wikidiff2_do_diff' ) ) {
+ return 'wikidiff2';
+ } else {
+ // Native PHP
+ return false;
+ }
+ }
+
+ /**
+ * Generates diff, to be wrapped internally in a logging/instrumentation
+ *
+ * @param string $otext Old text, must be already segmented
+ * @param string $ntext New text, must be already segmented
+ * @return bool|string
+ */
+ protected function textDiff( $otext, $ntext ) {
+ global $wgContLang;
+
+ $otext = str_replace( "\r\n", "\n", $otext );
+ $ntext = str_replace( "\r\n", "\n", $ntext );
+
+ $engine = $this->getEngine();
+
// Better external diff engine, the 2 may some day be dropped
// This one does the escaping and segmenting itself
- if ( function_exists( 'wikidiff2_do_diff' ) && $wgExternalDiffEngine === false ) {
+ if ( $engine === 'wikidiff2' ) {
$wikidiff2Version = phpversion( 'wikidiff2' );
if (
$wikidiff2Version !== false &&
$text .= $this->debug( 'wikidiff2' );
return $text;
- } elseif ( $wgExternalDiffEngine !== false && is_executable( $wgExternalDiffEngine ) ) {
+ } elseif ( $engine !== false ) {
# Diff via the shell
$tmpDir = wfTempDir();
$tempName1 = tempnam( $tmpDir, 'diff_' );
fwrite( $tempFile2, $ntext );
fclose( $tempFile1 );
fclose( $tempFile2 );
- $cmd = [ $wgExternalDiffEngine, $tempName1, $tempName2 ];
+ $cmd = [ $engine, $tempName1, $tempName2 ];
$result = Shell::command( $cmd )
->execute();
$exitCode = $result->getExitCode();
);
}
$difftext = $result->getStdout();
- $difftext .= $this->debug( "external $wgExternalDiffEngine" );
+ $difftext .= $this->debug( "external $engine" );
unlink( $tempName1 );
unlink( $tempName2 );
" -->\n";
}
+ /**
+ * Localise diff output
+ *
+ * @param string $text
+ * @return string
+ */
+ private function localiseDiff( $text ) {
+ $text = $this->localiseLineNumbers( $text );
+ if ( $this->getEngine() === 'wikidiff2' &&
+ version_compare( phpversion( 'wikidiff2' ), '1.5.1', '>=' )
+ ) {
+ $text = $this->addLocalisedTitleTooltips( $text );
+ }
+ return $text;
+ }
+
/**
* Replace line numbers with the text in the user's language
*
return $this->msg( 'lineno' )->numParams( $matches[1] )->escaped();
}
+ /**
+ * Add title attributes for tooltips on moved paragraph indicators
+ *
+ * @param string $text
+ * @return string
+ */
+ private function addLocalisedTitleTooltips( $text ) {
+ return preg_replace_callback(
+ '/class="mw-diff-movedpara-(left|right)"/',
+ [ $this, 'addLocalisedTitleTooltipsCb' ],
+ $text
+ );
+ }
+
+ /**
+ * @param array $matches
+ * @return string
+ */
+ private function addLocalisedTitleTooltipsCb( array $matches ) {
+ $key = $matches[1] === 'right' ?
+ 'diff-paragraph-moved-toold' :
+ 'diff-paragraph-moved-tonew';
+ return $matches[0] . ' title="' . $this->msg( $key )->escaped() . '"';
+ }
+
/**
* If there are revisions between the ones being compared, return a note saying so.
*