(bug 4974) Don't follow redirected talk page on "new messages" link
[lhc/web/wiklou.git] / includes / DifferenceEngine.php
index 178e967..be3db6e 100644 (file)
@@ -42,8 +42,9 @@ class DifferenceEngine {
         */
        function DifferenceEngine( $titleObj = null, $old = 0, $new = 0, $rcid = 0 ) {
                $this->mTitle = $titleObj;
+               wfDebug("DifferenceEngine old '$old' new '$new' rcid '$rcid'\n");
 
-               if ( 'prev' == $new ) {
+               if ( 'prev' === $new ) {
                        # Show diff between revision $old and the previous one.
                        # Get previous one from DB.
                        #
@@ -51,7 +52,7 @@ class DifferenceEngine {
 
                        $this->mOldid = $this->mTitle->getPreviousRevisionID( $this->mNewid );
 
-               } elseif ( 'next' == $new ) {
+               } elseif ( 'next' === $new ) {
                        # Show diff between revision $old and the previous one.
                        # Get previous one from DB.
                        #
@@ -105,15 +106,6 @@ CONTROL;
                        return;
                }
 
-               # 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();
-                       wfProfileOut( $fname );
-                       return;
-               }
-
                $t = $this->mTitle->getPrefixedText() . " (Diff: {$this->mOldid}, " .
                  "{$this->mNewid})";
                $mtext = wfMsg( 'missingarticle', "<nowiki>$t</nowiki>" );
@@ -129,6 +121,15 @@ CONTROL;
                        $wgOut->setArticleFlag( true );
                }
 
+               # 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();
+                       wfProfileOut( $fname );
+                       return;
+               }
+
                $wgOut->suppressQuickbar();
 
                $oldTitle = $this->mOldPage->getPrefixedText();
@@ -207,7 +208,7 @@ CONTROL;
                if( is_object( $this->mNewRev ) ) {
                        $wgOut->setRevisionId( $this->mNewRev->getId() );
                }
-               $wgOut->addWikiText( $this->mNewtext );
+               $wgOut->addSecondaryWikiText( $this->mNewtext );
 
                if( !$this->mNewRev->isCurrent() ) {
                        $wgOut->mParserOptions->setEditSection( $oldEditSectionSetting );
@@ -275,7 +276,7 @@ CONTROL;
                if( is_object( $this->mNewRev ) ) {
                        $wgOut->setRevisionId( $this->mNewRev->getId() );
                }
-               $wgOut->addWikiText( $this->mNewtext );
+               $wgOut->addSecondaryWikiText( $this->mNewtext );
 
                wfProfileOut( $fname );
        }
@@ -312,11 +313,14 @@ CONTROL;
 
        /**
         * Get the diff table body, without header
+        * Results are cached
         * Returns false on error
         */
        function getDiffBody() {
-               global $wgUseExternalDiffEngine, $wgContLang, $wgMemc, $wgDBname;
-
+               global $wgContLang, $wgMemc, $wgDBname;
+               $fname = 'DifferenceEngine::getDiffBody';
+               wfProfileIn( $fname );
+               
                // Cacheable?
                $key = false;
                if ( $this->mOldid && $this->mNewid ) {
@@ -325,44 +329,118 @@ CONTROL;
                        $difftext = $wgMemc->get( $key );
                        if ( $difftext ) {
                                wfIncrStats( 'diff_cache_hit' );
+                               $difftext = $this->localiseLineNumbers( $difftext );
+                               $difftext .= "\n<!-- diff cache key $key -->\n"; 
+                               wfProfileOut( $fname );
                                return $difftext;
                        }
                }
 
                if ( !$this->loadText() ) {
+                       wfProfileOut( $fname );
                        return false;
                }
 
-               $otext = $wgContLang->segmentForDiff($this->mOldtext);
-               $ntext = $wgContLang->segmentForDiff($this->mNewtext);
-               if ( $wgUseExternalDiffEngine ) {
-                       # For historical reasons, external diff engine expects
-                       # input text to be HTML-escaped already
-                       $otext = str_replace( "\r\n", "\n", htmlspecialchars ( $otext ) );
-                       $ntext = str_replace( "\r\n", "\n", htmlspecialchars ( $ntext ) );
-                       if( !function_exists( 'wikidiff_do_diff' ) ) {
-                               dl('php_wikidiff.so');
-                       }
-                       $difftext = wikidiff_do_diff( $otext, $ntext, 2 );
-               } else {
-                       $ota = explode( "\n", str_replace( "\r\n", "\n", $otext ) );
-                       $nta = explode( "\n", str_replace( "\r\n", "\n", $ntext ) );
-                       $diffs =& new Diff( $ota, $nta );
-                       $formatter =& new TableDiffFormatter();
-                       $difftext = $formatter->format( $diffs );
-               }
-               $difftext = $wgContLang->unsegmentForDiff($difftext);
-
+               $difftext = $this->generateDiffBody( $this->mOldtext, $this->mNewtext );
+               
                // Save to cache for 7 days
-               if ( $key !== false ) {
+               if ( $key !== false && $difftext !== false ) {
                        wfIncrStats( 'diff_cache_miss' );
                        $wgMemc->set( $key, $difftext, 7*86400 );
                } else {
                        wfIncrStats( 'diff_uncacheable' );
                }
+               // Replace line numbers with the text in the user's language
+               if ( $difftext !== false ) {
+                       $difftext = $this->localiseLineNumbers( $difftext );
+               }
+               wfProfileOut( $fname );
                return $difftext;
        }
 
+       /**
+        * Generate a diff, no caching
+        * $otext and $ntext must be already segmented
+        */
+       function generateDiffBody( $otext, $ntext ) {
+               global $wgExternalDiffEngine, $wgContLang;
+               $fname = 'DifferenceEngine::generateDiffBody';
+
+               $otext = str_replace( "\r\n", "\n", $otext );
+               $ntext = str_replace( "\r\n", "\n", $ntext );
+               
+               if ( $wgExternalDiffEngine == 'wikidiff' ) {
+                       # For historical reasons, external diff engine expects
+                       # input text to be HTML-escaped already
+                       $otext = htmlspecialchars ( $wgContLang->segmentForDiff( $otext ) );
+                       $ntext = htmlspecialchars ( $wgContLang->segmentForDiff( $ntext ) );
+                       if( !function_exists( 'wikidiff_do_diff' ) ) {
+                               dl('php_wikidiff.so');
+                       }
+                       return $wgContLang->unsegementForDiff( wikidiff_do_diff( $otext, $ntext, 2 ) );
+               } 
+               
+               if ( $wgExternalDiffEngine == 'wikidiff2' ) {
+                       # 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' ) ) {
+                               @dl('php_wikidiff2.so');
+                       }
+                       if ( function_exists( 'wikidiff2_do_diff' ) ) {
+                               return wikidiff2_do_diff( $otext, $ntext, 2 );
+                       }
+               }
+               if ( $wgExternalDiffEngine !== false ) {
+                       # Diff via the shell
+                       global $wgTmpDirectory;
+                       $tempName1 = tempnam( $wgTmpDirectory, 'diff_' );
+                       $tempName2 = tempnam( $wgTmpDirectory, 'diff_' );
+
+                       $tempFile1 = fopen( $tempName1, "w" );
+                       if ( !$tempFile1 ) {
+                               wfProfileOut( $fname );
+                               return false;
+                       }
+                       $tempFile2 = fopen( $tempName2, "w" );
+                       if ( !$tempFile2 ) {
+                               wfProfileOut( $fname );
+                               return false;
+                       }
+                       fwrite( $tempFile1, $otext );
+                       fwrite( $tempFile2, $ntext );
+                       fclose( $tempFile1 );
+                       fclose( $tempFile2 );
+                       $cmd = wfEscapeShellArg( $wgExternalDiffEngine, $tempName1, $tempName2 );
+                       wfProfileIn( "$fname-shellexec" );
+                       $difftext = wfShellExec( $cmd );
+                       wfProfileOut( "$fname-shellexec" );
+                       unlink( $tempName1 );
+                       unlink( $tempName2 );
+                       return $difftext;
+               }
+               
+               # Native PHP diff
+               $ota = explode( "\n", $wgContLang->segmentForDiff( $otext ) );
+               $nta = explode( "\n", $wgContLang->segmentForDiff( $ntext ) );
+               $diffs =& new Diff( $ota, $nta );
+               $formatter =& new TableDiffFormatter();
+               return $wgContLang->unsegmentForDiff( $formatter->format( $diffs ) );
+       }
+               
+
+       /**
+        * Replace line numbers with the text in the user's language
+        */
+       function localiseLineNumbers( $text ) {
+               return preg_replace_callback( '/<!--LINE (\d+)-->/', 
+                       array( &$this, 'localiseLineNumbersCb' ), $text );
+       }
+
+       function localiseLineNumbersCb( $matches ) {
+               global $wgLang;
+               return wfMsg( 'lineno', $wgLang->formatNum( $matches[1] ) );
+       }
+
        /**
         * Add the header to a diff body
         */
@@ -440,8 +518,15 @@ CONTROL;
                if( $this->mOldid ) {
                        $this->mOldRev = Revision::newFromId( $this->mOldid );
                } elseif ( $this->mOldid === 0 ) {
-                       $this->mOldRev = $this->mNewRev->getPrevious();
-                       $this->mOldid = $this->mOldRev->getId();
+                       $rev = $this->mNewRev->getPrevious();
+                       if( $rev ) {
+                               $this->mOldid = $rev->getId();
+                               $this->mOldRev = $rev;
+                       } else {
+                               // No previous revision; mark to show as first-version only.
+                               $this->mOldid = false;
+                               $this->mOldRev = false;
+                       }
                }/* elseif ( $this->mOldid === false ) leave mOldRev false; */
 
                if( is_null( $this->mOldRev ) ) {
@@ -479,9 +564,15 @@ CONTROL;
                }
                if ( $this->mOldRev ) {
                        $this->mOldtext = $this->mOldRev->getText();
+                       if ( $this->mOldtext === false ) {
+                               return false;
+                       }
                }
                if ( $this->mNewRev ) {
                        $this->mNewtext = $this->mNewRev->getText();
+                       if ( $this->mNewtext === false ) {
+                               return false;
+                       }
                }
                return true;
        }
@@ -1586,11 +1677,8 @@ class TableDiffFormatter extends DiffFormatter
        }
 
        function _block_header( $xbeg, $xlen, $ybeg, $ylen ) {
-               $l1 = wfMsg( 'lineno', $xbeg );
-               $l2 = wfMsg( 'lineno', $ybeg );
-
-               $r = '<tr><td colspan="2" align="left"><strong>'.$l1."</strong></td>\n" .
-                 '<td colspan="2" align="left"><strong>'.$l2."</strong></td></tr>\n";
+               $r = '<tr><td colspan="2" align="left"><strong><!--LINE '.$xbeg."--></strong></td>\n" .
+                 '<td colspan="2" align="left"><strong><!--LINE '.$ybeg."--></strong></td></tr>\n";
                return $r;
        }