lib/TWiki/UI/RDiff.pm
author Colas Nahaboo <colas@nahaboo.net>
Sat, 26 Jan 2008 15:50:53 +0100
changeset 0 414e01d06fd5
child 1 e2915a7cbdfa
permissions -rw-r--r--
RELEASE 4.2.0 freetown
colas@0
     1
# Module of TWiki Enterprise Collaboration Platform, http://TWiki.org/
colas@0
     2
#
colas@0
     3
# Copyright (C) 1999-2007 TWiki Contributors.
colas@0
     4
# All Rights Reserved. TWiki Contributors
colas@0
     5
# are listed in the AUTHORS file in the root of this distribution.
colas@0
     6
# NOTE: Please extend that file, not this notice.
colas@0
     7
#
colas@0
     8
# This program is free software; you can redistribute it and/or
colas@0
     9
# modify it under the terms of the GNU General Public License
colas@0
    10
# as published by the Free Software Foundation; either version 2
colas@0
    11
# of the License, or (at your option) any later version. For
colas@0
    12
# more details read LICENSE in the root of this distribution.
colas@0
    13
#
colas@0
    14
# This program is distributed in the hope that it will be useful,
colas@0
    15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
colas@0
    16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
colas@0
    17
#
colas@0
    18
# As per the GPL, removal of this notice is prohibited.
colas@0
    19
colas@0
    20
=begin twiki
colas@0
    21
colas@0
    22
---+ package TWiki::UI::RDiff
colas@0
    23
colas@0
    24
UI functions for diffing.
colas@0
    25
colas@0
    26
=cut
colas@0
    27
colas@0
    28
package TWiki::UI::RDiff;
colas@0
    29
colas@0
    30
use strict;
colas@0
    31
use Assert;
colas@0
    32
use Error qw( :try );
colas@0
    33
colas@0
    34
require TWiki;
colas@0
    35
require TWiki::UI;
colas@0
    36
colas@0
    37
#TODO: this needs to be exposed to plugins and whoever might want to over-ride the rendering of diffs
colas@0
    38
#Hash, indexed by diffType (+,-,c,u,l.....)
colas@0
    39
#contains {colour, CssClassName}
colas@0
    40
my %format =
colas@0
    41
  (
colas@0
    42
   '+' => [ '#ccccff', 'twikiDiffAddedMarker'],
colas@0
    43
   '-' => [ '#ff9999', 'twikiDiffDeletedMarker'],
colas@0
    44
   'c' => [ '#99ff99', 'twikiDiffChangedText'],
colas@0
    45
   'u' => [ '#ffffff', 'twikiDiffUnchangedText'],
colas@0
    46
   'l' => [ '#eeeeee', 'twikiDiffLineNumberHeader']
colas@0
    47
  );
colas@0
    48
colas@0
    49
#SVEN - new design.
colas@0
    50
#main gets the info (NO MAJOR CHANGES NEEDED)
colas@0
    51
#parseDiffs reads the diffs and interprets the information into types {"+", "-", "u", "c", "l"} (add, remove, unchanged, changed, lineNumber} where line number is for diffs that skip unchanged lines (diff -u etc)
colas@0
    52
#so renderDiffs would get an array of [changeType, $oldstring, $newstring] 
colas@0
    53
#		corresponding to Algorithm::Diff's output
colas@0
    54
#renderDiffs iterates through the interpreted info and makes it into TML / HTML? (mmm)
colas@0
    55
#and can be over-ridden :)
colas@0
    56
#(now can we do this in a way that automagically can cope eith word / letter based diffs?)
colas@0
    57
#NOTE: if we do our own diffs in perl we can go straight to renderDiffs
colas@0
    58
#TODO: I'm starting to think that we should have a variable number of lines of context. more context if you are doing a 1.13 tp 1.14 diff, less when you do a show page history.
colas@0
    59
#TODO: ***URGENT*** the diff rendering dies badly when you have table cell changes and context
colas@0
    60
#TODO: ?type={history|diff} so that you can do a normal diff between r1.3 and r1.32 (rather than a history) (and when doing a history, we maybe should not expand %SEARCH...
colas@0
    61
colas@0
    62
#| Description: | twiki render a cell of data from a Diff |
colas@0
    63
#| Parameter: =$data= |  |
colas@0
    64
#| Parameter: =$topic= |  |
colas@0
    65
#| Return: =$text= | Formatted html text |
colas@0
    66
#| TODO: | this should move to Render.pm |
colas@0
    67
sub _renderCellData {
colas@0
    68
    my( $session, $data, $web, $topic ) = @_;
colas@0
    69
    ASSERT($topic) if DEBUG;
colas@0
    70
    if ( $data ){
colas@0
    71
        $data =~ s(^%META:FIELD{(.*)}%.*$)
colas@0
    72
          (_renderAttrs($1,'|*FORM FIELD $title*|$name|$value|'))gem;
colas@0
    73
        $data =~ s(^%META:([A-Z]+){(.*)}%$)
colas@0
    74
          ('|*META '.$1.'*|'._renderAttrs($2).'|')gem;
colas@0
    75
colas@0
    76
        $data = $session->handleCommonTags( $data, $web, $topic );
colas@0
    77
        $data = $session->renderer->getRenderedVersion( $data, $web, $topic );
colas@0
    78
        # Match up table tags, remove comments
colas@0
    79
        if( $data =~ m/<\/?(th|td|table)\b/i ) {
colas@0
    80
            # data has <th> or <td>, need to fix ables
colas@0
    81
            my $bTable = ( $data =~ s/(<table)/$1/gois ) || 0;
colas@0
    82
            my $eTable = ( $data =~ s/(<\/table)/$1/gois ) || 0;
colas@0
    83
            while( $eTable < $bTable ) {
colas@0
    84
                $data .= CGI::end_table();
colas@0
    85
                $eTable++;
colas@0
    86
            }
colas@0
    87
            while( $bTable < $eTable ) {
colas@0
    88
                $data = CGI::start_table().$data;
colas@0
    89
                $bTable++;
colas@0
    90
            }
colas@0
    91
            unless( $bTable ) {
colas@0
    92
                $data = CGI::start_table().$data.CGI::end_table();
colas@0
    93
            }
colas@0
    94
        }
colas@0
    95
        # unhide html comments (<!-- --> type tags)
colas@0
    96
        $data =~  s/<!--(.*?)-->/<pre>&lt;--$1--&gt;<\/pre>/gos;
colas@0
    97
    }
colas@0
    98
    return $data;
colas@0
    99
}
colas@0
   100
colas@0
   101
# Simple method to expand attribute values in a format string
colas@0
   102
sub _renderAttrs {
colas@0
   103
    my( $p, $f) = @_;
colas@0
   104
    require TWiki::Attrs;
colas@0
   105
    my $attrs = new TWiki::Attrs( $p );
colas@0
   106
    require TWiki::Store;
colas@0
   107
    if( $f ) {
colas@0
   108
        for my $key ( keys %$attrs ) {
colas@0
   109
            my $av = TWiki::Store::dataDecode( $attrs->{$key} );
colas@0
   110
            $f =~ s/\$$key\b/$av/g;
colas@0
   111
        }
colas@0
   112
    } else {
colas@0
   113
        $f = $attrs->stringify();
colas@0
   114
    }
colas@0
   115
    return $f;
colas@0
   116
}
colas@0
   117
colas@0
   118
sub _sideBySideRow {
colas@0
   119
    my( $left, $right, $lc, $rc ) = @_;
colas@0
   120
colas@0
   121
    my $d1 = CGI::td({ bgcolor=>$format{$lc}[0],
colas@0
   122
                       class=>$format{$lc}[1],
colas@0
   123
                       valign=>'top'}, $left.'&nbsp;' );
colas@0
   124
    my $d2 = CGI::td({ bgcolor=>$format{$rc}[0],
colas@0
   125
                       class=>$format{$rc}[1],
colas@0
   126
                       valign=>'top'}, $right.'&nbsp;' );
colas@0
   127
    return CGI::Tr( $d1 . $d2 );
colas@0
   128
}
colas@0
   129
colas@0
   130
#| Description: | render the Diff entry using side by side |
colas@0
   131
#| Parameter: =$diffType= | {+,-,u,c,l} denotes the patch operation |
colas@0
   132
#| Parameter: =$left= | the text blob before the opteration |
colas@0
   133
#| Parameter: =$right= | the text after the operation |
colas@0
   134
#| Return: =$result= | Formatted html text |
colas@0
   135
#| TODO: | this should move to Render.pm |
colas@0
   136
sub _renderSideBySide
colas@0
   137
{
colas@0
   138
    my ( $session, $web, $topic, $diffType, $left, $right ) = @_;
colas@0
   139
    my $result = '';
colas@0
   140
colas@0
   141
    $left = _renderCellData( $session, $left, $web, $topic );
colas@0
   142
    $right = _renderCellData( $session, $right, $web, $topic );
colas@0
   143
colas@0
   144
    if ( $diffType eq '-') {
colas@0
   145
        $result .= _sideBySideRow( $left, $right, '-', 'u' )
colas@0
   146
    } elsif ( $diffType eq "+") {
colas@0
   147
        $result .= _sideBySideRow( $left, $right, 'u', '+' )
colas@0
   148
    } elsif ( $diffType eq "u") {
colas@0
   149
        $result .= _sideBySideRow( $left, $right, 'u', 'u' )
colas@0
   150
    } elsif ( $diffType eq "c") {
colas@0
   151
        $result .= _sideBySideRow( $left, $right, 'c', 'c' )
colas@0
   152
    } elsif ( $diffType eq "l" && $left ne '' && $right ne '' ) {
colas@0
   153
        $result .= CGI::Tr({
colas@0
   154
                            bgcolor=>$format{l}[0],
colas@0
   155
                            class=>$format{l}[1],
colas@0
   156
                           },
colas@0
   157
                           CGI::th({align=>'center'},
colas@0
   158
                                   ($session->i18n->maketext('Line: [_1]',$left))).
colas@0
   159
                           CGI::th({align=>'center'},
colas@0
   160
                                   ($session->i18n->maketext('Line: [_1]',$right)))
colas@0
   161
                          );
colas@0
   162
    }
colas@0
   163
    # unhide html comments (<!-- --> type tags)
colas@0
   164
    $result =~  s/<!--(.*?)-->/<pre>&lt;--$1--&gt;<\/pre>/gos;
colas@0
   165
colas@0
   166
    return $result;
colas@0
   167
}
colas@0
   168
colas@0
   169
#| Description: | render the Diff array (no TML conversion) |
colas@0
   170
#| Parameter: =$diffType= | {+,-,u,c,l} denotes the patch operation |
colas@0
   171
#| Parameter: =$left= | the text blob before the opteration |
colas@0
   172
#| Parameter: =$right= | the text after the operation |
colas@0
   173
#| Return: =$result= | Formatted html text |
colas@0
   174
#| TODO: | this should move to Render.pm |
colas@0
   175
sub _renderDebug
colas@0
   176
{
colas@0
   177
    my ( $diffType, $left, $right ) = @_;
colas@0
   178
    my $result = '';
colas@0
   179
colas@0
   180
    #de-html-ize
colas@0
   181
    $left =~ s/&/&amp;/go;
colas@0
   182
    $left =~ s/</&lt;/go;
colas@0
   183
    $left =~ s/>/&gt;/go;
colas@0
   184
    $right =~ s/&/&amp;/go;
colas@0
   185
    $right =~ s/</&lt;/go;
colas@0
   186
    $right =~ s/>/&gt;/go;
colas@0
   187
	
colas@0
   188
	$result = CGI::Tr(
colas@0
   189
		CGI::td( 'type: '. $diffType ));
colas@0
   190
		
colas@0
   191
	my %classMap =
colas@0
   192
	  (
colas@0
   193
	   '+' => [ 'twikiDiffAddedText'],
colas@0
   194
	   '-' => [ 'twikiDiffDeletedText'],
colas@0
   195
	   'c' => [ 'twikiDiffChangedText'],
colas@0
   196
	   'u' => [ 'twikiDiffUnchangedText'],
colas@0
   197
	   'l' => [ 'twikiDiffLineNumberHeader']
colas@0
   198
	  );
colas@0
   199
  
colas@0
   200
  	my $styleClass = ' '.$classMap{$diffType}[0] || '';
colas@0
   201
  	my $styleClassLeft = ($diffType ne 'c') ? $styleClass : '';
colas@0
   202
  	my $styleClassRight = $styleClass;
colas@0
   203
  	
colas@0
   204
	if ($diffType ne '+') {
colas@0
   205
   	    $result .= CGI::Tr( {class=>'twikiDiffDebug'},
colas@0
   206
		    CGI::td( {class=>'twikiDiffDebugLeft '.$styleClassLeft}, CGI::div( $left) ));
colas@0
   207
	}
colas@0
   208
	if (($diffType ne '-') && ($diffType ne 'l')) {
colas@0
   209
	    $result .= CGI::Tr( {class=>'twikiDiffDebug'},
colas@0
   210
		    CGI::td( {class=>'twikiDiffDebugRight '.$styleClassRight}, CGI::div( $right) ));
colas@0
   211
	}
colas@0
   212
        # unhide html comments (<!-- --> type tags)
colas@0
   213
        $result =~  s/<!--(.*?)-->/<pre>&lt;--$1--&gt;<\/pre>/gos;
colas@0
   214
colas@0
   215
    return $result;
colas@0
   216
}
colas@0
   217
colas@0
   218
sub _sequentialRow {
colas@0
   219
    my( $bg, $hdrcls, $bodycls, $data, $code, $char, $session ) = @_;
colas@0
   220
    my $row = '';
colas@0
   221
    if( $char ) {
colas@0
   222
        $row = CGI::td({bgcolor=>$format{$code}[0],
colas@0
   223
                        class=>$format{$code}[1],
colas@0
   224
                        valign=>'top',
colas@0
   225
                        width=>"1%"},
colas@0
   226
                       $char.CGI::br().$char);
colas@0
   227
    } else {
colas@0
   228
      $row = CGI::td( "&nbsp;" );
colas@0
   229
    }
colas@0
   230
    $row .= CGI::td({class=>"twikiDiff${bodycls}Text"}, $data);
colas@0
   231
    $row = CGI::Tr( $row );
colas@0
   232
    if( $bg ) {
colas@0
   233
        return CGI::Tr(CGI::td({bgcolor=>$bg,
colas@0
   234
                                class=>"twikiDiff${hdrcls}Header",
colas@0
   235
                                colspan=>9},
colas@0
   236
                               CGI::b( $session->i18n->maketext($hdrcls).': '))).$row;
colas@0
   237
    } else {
colas@0
   238
        return $row;
colas@0
   239
    }
colas@0
   240
}
colas@0
   241
colas@0
   242
#| Description: | render the Diff using old style sequential blocks |
colas@0
   243
#| Parameter: =$diffType= | {+,-,u,c,l} denotes the patch operation |
colas@0
   244
#| Parameter: =$left= | the text blob before the opteration |
colas@0
   245
#| Parameter: =$right= | the text after the operation |
colas@0
   246
#| Return: =$result= | Formatted html text |
colas@0
   247
#| TODO: | this should move to Render.pm |
colas@0
   248
sub _renderSequential
colas@0
   249
{
colas@0
   250
    my ( $session, $web, $topic, $diffType, $left, $right ) = @_;
colas@0
   251
    my $result = '';
colas@0
   252
    ASSERT($topic) if DEBUG;
colas@0
   253
colas@0
   254
    #note: I have made the colspan 9 to make sure that it spans all columns (thought there are only 2 now)
colas@0
   255
    if ( $diffType eq '-') {
colas@0
   256
        $result .=
colas@0
   257
          _sequentialRow( '#FFD7D7',
colas@0
   258
                          'Deleted',
colas@0
   259
                          'Deleted',
colas@0
   260
                          _renderCellData( $session, $left, $web, $topic ),
colas@0
   261
                          '-', '&lt;',
colas@0
   262
                          $session);
colas@0
   263
    } elsif ( $diffType eq '+') {
colas@0
   264
        $result .=
colas@0
   265
          _sequentialRow( '#D0FFD0',
colas@0
   266
                          'Added',
colas@0
   267
                          'Added',
colas@0
   268
                          _renderCellData( $session, $right, $web, $topic ),
colas@0
   269
                          '+', '&gt;',
colas@0
   270
                          $session );
colas@0
   271
    } elsif ( $diffType eq 'u') {
colas@0
   272
        $result .=
colas@0
   273
          _sequentialRow( undef,
colas@0
   274
                          'Unchanged',
colas@0
   275
                          'Unchanged',
colas@0
   276
                          _renderCellData( $session, $right, $web, $topic ),
colas@0
   277
                          'u', '',
colas@0
   278
                          $session );
colas@0
   279
    } elsif ( $diffType eq 'c') {
colas@0
   280
        $result .=
colas@0
   281
          _sequentialRow( '#D0FFD0',
colas@0
   282
                          'Changed',
colas@0
   283
                          'Deleted',
colas@0
   284
                          _renderCellData( $session, $left, $web, $topic ),
colas@0
   285
                          '-', '&lt;',
colas@0
   286
                          $session );
colas@0
   287
        $result .=
colas@0
   288
          _sequentialRow( undef,
colas@0
   289
                          'Changed',
colas@0
   290
                          'Added',
colas@0
   291
                          _renderCellData( $session, $right, $web, $topic ),
colas@0
   292
                          '+', '&gt;',
colas@0
   293
                          $session );
colas@0
   294
    } elsif ( $diffType eq 'l' && $left ne '' && $right ne '' ) {
colas@0
   295
        $result .= CGI::Tr({bgcolor=>$format{l}[0],
colas@0
   296
                            class=>'twikiDiffLineNumberHeader'},
colas@0
   297
                           CGI::th({align=>'left',
colas@0
   298
                                    colspan=>9},
colas@0
   299
                                    ($session->i18n->maketext('Line: [_1] to [_2]',$left,$right))
colas@0
   300
                                  )
colas@0
   301
                           );
colas@0
   302
    }
colas@0
   303
colas@0
   304
    # unhide html comments (<!-- --> type tags)
colas@0
   305
    $result =~  s/<!--(.*?)-->/<pre>&lt;--$1--&gt;<\/pre>/gos;
colas@0
   306
colas@0
   307
    return $result;
colas@0
   308
}
colas@0
   309
colas@0
   310
#| Description: | uses renderStyle to choose the rendering function to use |
colas@0
   311
#| Parameter: =$diffArray= | array generated by parseRevisionDiff |
colas@0
   312
#| Parameter: =$renderStyle= | style of rendering { debug, sequential, sidebyside} |
colas@0
   313
#| Return: =$text= | output html for one renderes revision diff |
colas@0
   314
#| TODO: | move into Render.pm |
colas@0
   315
sub _renderRevisionDiff
colas@0
   316
{
colas@0
   317
    my( $session, $web, $topic, $sdiffArray_ref, $renderStyle ) = @_;
colas@0
   318
colas@0
   319
#combine sequential array elements that are the same diffType
colas@0
   320
    my @diffArray = ();
colas@0
   321
	foreach my $ele ( @$sdiffArray_ref ) {
colas@0
   322
		if( ( @$ele[1] =~ /^\%META\:TOPICINFO/ ) || ( @$ele[2] =~ /^\%META\:TOPICINFO/ ) ) {
colas@0
   323
			# do nothing, ignore redundant topic info
colas@0
   324
			# FIXME: Intelligently remove followup lines in case META:TOPICINFO is the only change
colas@0
   325
		} elsif( ( @diffArray ) && ( @{$diffArray[$#diffArray]}[0] eq @$ele[0] ) ) {
colas@0
   326
			@{$diffArray[$#diffArray]}[1] .= "\n".@$ele[1];
colas@0
   327
			@{$diffArray[$#diffArray]}[2] .= "\n".@$ele[2];
colas@0
   328
		} else {
colas@0
   329
			# Store doesn't expand REVINFO and we don't have rev info available now; escape tags to avoid confusion
colas@0
   330
			@$ele[1] =~ s/\%REVINFO/\%<nop>REVINFO/ unless $renderStyle eq 'debug';
colas@0
   331
			@$ele[2] =~ s/\%REVINFO/\%<nop>REVINFO/ unless $renderStyle eq 'debug';
colas@0
   332
			push @diffArray, $ele;
colas@0
   333
		}
colas@0
   334
	}
colas@0
   335
	my $diffArray_ref = \@diffArray;
colas@0
   336
colas@0
   337
colas@0
   338
    my $result = "";
colas@0
   339
    my $data = '';
colas@0
   340
    my $diff_ref = undef;
colas@0
   341
    for my $next_ref ( @$diffArray_ref ) {
colas@0
   342
    	if (( @$next_ref[0] eq 'l' ) && ( @$next_ref[1] eq 0 ) && (@$next_ref[2] eq 0)) {
colas@0
   343
            next;
colas@0
   344
		}
colas@0
   345
		if (! $diff_ref ) {
colas@0
   346
            $diff_ref = $next_ref;
colas@0
   347
            next;
colas@0
   348
		}
colas@0
   349
		if (( @$diff_ref[0] eq '-' ) && ( @$next_ref[0] eq '+' )) {
colas@0
   350
		    $diff_ref = ['c', @$diff_ref[1], @$next_ref[2]];
colas@0
   351
            $next_ref = undef;
colas@0
   352
		}
colas@0
   353
		if ( $renderStyle eq 'sequential' ) {
colas@0
   354
		    $result .= _renderSequential( $session, $web, $topic, @$diff_ref );
colas@0
   355
		} elsif ( $renderStyle eq 'sidebyside' ) {
colas@0
   356
            $result .= CGI::Tr(CGI::td({ width=>'50%'}, ''),
colas@0
   357
                               CGI::td({ width=>'50%'}, ''));
colas@0
   358
		    $result .= _renderSideBySide( $session, $web, $topic, @$diff_ref );
colas@0
   359
		} elsif ( $renderStyle eq 'debug' ) {
colas@0
   360
		    $result .= _renderDebug( @$diff_ref );
colas@0
   361
		}
colas@0
   362
		$diff_ref = $next_ref;
colas@0
   363
	}
colas@0
   364
    #don't forget the last one ;)
colas@0
   365
    if ( $diff_ref ) {
colas@0
   366
        if ( $renderStyle eq 'sequential' ) {
colas@0
   367
            $result .= _renderSequential ( $session, $web, $topic, @$diff_ref );
colas@0
   368
        } elsif ( $renderStyle eq 'sidebyside' ) {
colas@0
   369
            $result .= CGI::Tr(CGI::td({ width=>'50%'}, ''),
colas@0
   370
                               CGI::td({ width=>'50%'}, ''));
colas@0
   371
            $result .= _renderSideBySide( $session, $web, $topic, @$diff_ref );
colas@0
   372
        } elsif ( $renderStyle eq 'debug' ) {
colas@0
   373
            $result .= _renderDebug( @$diff_ref );
colas@0
   374
        }
colas@0
   375
    }
colas@0
   376
    return CGI::table( { class => 'twikiDiffTable',
colas@0
   377
                         width => '100%',
colas@0
   378
                         cellspacing => 0,
colas@0
   379
                         cellpadding => 0}, $result );
colas@0
   380
}
colas@0
   381
colas@0
   382
=pod
colas@0
   383
colas@0
   384
---++ StaticMethod diff( $session, $web, $topic, $query )
colas@0
   385
colas@0
   386
=diff= command handler.
colas@0
   387
This method is designed to be
colas@0
   388
invoked via the =UI::run= method.
colas@0
   389
colas@0
   390
Renders the differences between version of a TwikiTopic
colas@0
   391
| topic | topic that we are showing the differences of |
colas@0
   392
| rev1 | the higher revision |
colas@0
   393
| rev2 | the lower revision |
colas@0
   394
| render | the rendering style {sequential, sidebyside, raw, debug} | (preferences) DIFFRENDERSTYLE, =sequential= |
colas@0
   395
| type | {history, diff, last} history diff, version to version, last version to previous | =history= |
colas@0
   396
| context | number of lines of context |
colas@0
   397
| skin | the skin(s) to use to display the diff |
colas@0
   398
TODO:
colas@0
   399
   * add a {word} render style
colas@0
   400
   * move the common CGI param handling to one place
colas@0
   401
   * move defaults somewhere
colas@0
   402
colas@0
   403
=cut
colas@0
   404
colas@0
   405
sub diff {
colas@0
   406
    my $session = shift;
colas@0
   407
colas@0
   408
    my $query = $session->{cgiQuery};
colas@0
   409
    my $webName = $session->{webName};
colas@0
   410
    my $topic = $session->{topicName};
colas@0
   411
colas@0
   412
    TWiki::UI::checkWebExists( $session, $webName, $topic, 'diff' );
colas@0
   413
    TWiki::UI::checkTopicExists( $session, $webName, $topic, 'diff' );
colas@0
   414
colas@0
   415
    my $renderStyle = $query->param('render') ||
colas@0
   416
      $session->{prefs}->getPreferencesValue( 'DIFFRENDERSTYLE' ) ||
colas@0
   417
        'sequential';
colas@0
   418
    my $diffType = $query->param('type') || 'history';
colas@0
   419
    my $contextLines = $query->param('context');
colas@0
   420
    unless( defined $contextLines ) {
colas@0
   421
        $session->{prefs}->getPreferencesValue( 'DIFFCONTEXTLINES' );
colas@0
   422
        $contextLines = 3 unless defined $contextLines;
colas@0
   423
    }
colas@0
   424
    my $rev1 = $query->param( 'rev1' );
colas@0
   425
    my $rev2 = $query->param( 'rev2' );
colas@0
   426
colas@0
   427
    my $skin = $session->getSkin();
colas@0
   428
    my $tmpl = $session->templates->readTemplate( 'rdiff', $skin );
colas@0
   429
    $tmpl =~ s/\%META{.*?}\%//go;  # remove %META{'parent'}%
colas@0
   430
colas@0
   431
    my( $before, $difftmpl, $after, $tail) = split( /%REPEAT%/, $tmpl);
colas@0
   432
colas@0
   433
    $before ||= '';
colas@0
   434
    $after ||= '';
colas@0
   435
    $tail ||= '';
colas@0
   436
colas@0
   437
    my $maxrev = $session->{store}->getRevisionNumber( $webName, $topic );
colas@0
   438
    $maxrev =~ s/r?1\.//go;  # cut 'r' and major
colas@0
   439
colas@0
   440
    if ( $diffType eq 'last' ) {
colas@0
   441
        $rev1 = $maxrev;
colas@0
   442
        $rev2 = $maxrev-1;
colas@0
   443
    }
colas@0
   444
colas@0
   445
    $rev1 = $session->{store}->cleanUpRevID( $rev1 );
colas@0
   446
    $rev1 = $maxrev if( $rev1 < 1 );
colas@0
   447
    $rev1 = $maxrev if( $rev1 > $maxrev );
colas@0
   448
colas@0
   449
    $rev2 = $session->{store}->cleanUpRevID( $rev2 );
colas@0
   450
    $rev2 = 1 if( $rev2 < 1 );
colas@0
   451
    $rev2 = $maxrev if( $rev2 > $maxrev );
colas@0
   452
colas@0
   453
    my $revTitle1 = $rev1;
colas@0
   454
    my $revTitle2 = ( $rev1 != $rev2 ) ? $rev2 : '';
colas@0
   455
colas@0
   456
    $before =~ s/%REVTITLE1%/$revTitle1/go;
colas@0
   457
    $before =~ s/%REVTITLE2%/$revTitle2/go;
colas@0
   458
    $before = $session->handleCommonTags( $before, $webName, $topic );
colas@0
   459
    $before = $session->renderer->getRenderedVersion( $before, $webName, $topic );
colas@0
   460
colas@0
   461
    my $page = $before;
colas@0
   462
colas@0
   463
    # do one or more diffs
colas@0
   464
    $difftmpl = $session->handleCommonTags( $difftmpl, $webName, $topic );
colas@0
   465
    my $r1 = $rev1;
colas@0
   466
    my $r2 = $rev2;
colas@0
   467
    my $isMultipleDiff = 0;
colas@0
   468
colas@0
   469
    if (( $diffType eq 'history' ) && ( $r1 > $r2 + 1)) {
colas@0
   470
        $r2 = $r1 - 1;
colas@0
   471
        $isMultipleDiff = 1;
colas@0
   472
    }
colas@0
   473
colas@0
   474
    do {
colas@0
   475
        my $diff = $difftmpl;
colas@0
   476
        $diff =~ s/%REVTITLE1%/$r1/go;
colas@0
   477
colas@0
   478
        my $rInfo = '';
colas@0
   479
        my $rInfo2 = '';
colas@0
   480
        my $text;
colas@0
   481
        if ( $r1 > $r2 + 1) {
colas@0
   482
            $rInfo = $session->i18n->maketext(
colas@0
   483
                "Changes from r[_1] to r[_2]", $r2, $r1);
colas@0
   484
        } else {
colas@0
   485
            $rInfo = $session->renderer->renderRevisionInfo(
colas@0
   486
                $webName, $topic, undef, $r1, '$date - $wikiusername' );
colas@0
   487
            $rInfo2 = $session->renderer->renderRevisionInfo(
colas@0
   488
                $webName, $topic, undef, $r1, '$rev ($date - $time) - $wikiusername' );
colas@0
   489
        }
colas@0
   490
        # eliminate white space to prevent wrap around in HR table:
colas@0
   491
        $rInfo =~ s/\s+/&nbsp;/g;
colas@0
   492
        $rInfo2 =~ s/\s+/&nbsp;/g;
colas@0
   493
        my $diffArrayRef = $session->{store}->getRevisionDiff(
colas@0
   494
            $session->{user}, $webName, $topic, $r2, $r1, $contextLines );
colas@0
   495
        $text = _renderRevisionDiff( $session, $webName, $topic,
colas@0
   496
                                     $diffArrayRef, $renderStyle );
colas@0
   497
        $diff =~ s/%REVINFO1%/$rInfo/go;
colas@0
   498
        $diff =~ s/%REVINFO2%/$rInfo2/go;
colas@0
   499
        $diff =~ s/%TEXT%/$text/go;
colas@0
   500
        $page .= $diff;
colas@0
   501
        $r1 = $r1 - 1;
colas@0
   502
        $r2 = $r2 - 1;
colas@0
   503
        $r2 = 1 if( $r2 < 1 );
colas@0
   504
    } while( $diffType eq 'history' && ( $r1 > $rev2 || $r1 == 1 ));
colas@0
   505
colas@0
   506
    if( $TWiki::cfg{Log}{rdiff} ) {
colas@0
   507
        $session->writeLog( 'rdiff', $webName.'.'.$topic, "$rev1 $rev2" );
colas@0
   508
    }
colas@0
   509
colas@0
   510
    my $i = $maxrev;
colas@0
   511
    my $j = $maxrev;
colas@0
   512
    my $revisions = '';
colas@0
   513
    my $breakRev = 0;
colas@0
   514
    if( $TWiki::cfg{NumberOfRevisions} > 0 &&
colas@0
   515
        $TWiki::cfg{NumberOfRevisions} < $maxrev ) {
colas@0
   516
        $breakRev = $maxrev - $TWiki::cfg{NumberOfRevisions} + 1;
colas@0
   517
    }
colas@0
   518
    
colas@0
   519
    #SMELL: this should be the same variable as in view script, and so on - thus be configurable
colas@0
   520
    my $revSeperator = '&lt;';
colas@0
   521
colas@0
   522
    while( $i > 0 ) {
colas@0
   523
        $revisions .= ' '.
colas@0
   524
          CGI::a( { href=>$session->getScriptUrl( 0, 'view', $webName, $topic,
colas@0
   525
                                                  rev => $i ),
colas@0
   526
                    rel => 'nofollow' }, 'r'.$i);
colas@0
   527
        if( $i != 1 ) {
colas@0
   528
            if( $i == $breakRev ) {
colas@0
   529
                $i = 1;
colas@0
   530
            } else {
colas@0
   531
                if( ( $i == $rev1 ) && ( !$isMultipleDiff ) ) {
colas@0
   532
                    $revisions .= ' '.$revSeperator;
colas@0
   533
                } else {
colas@0
   534
                    $j = $i - 1;
colas@0
   535
                    $revisions .= ' '.
colas@0
   536
                      CGI::a( { href=>$session->getScriptUrl(
colas@0
   537
                          0, 'rdiff', $webName, $topic,
colas@0
   538
                          rev1 => $i, rev2 => $j ),
colas@0
   539
                                rel => 'nofollow' },
colas@0
   540
                              $revSeperator);
colas@0
   541
                }
colas@0
   542
            }
colas@0
   543
        }
colas@0
   544
        $i--;
colas@0
   545
    }
colas@0
   546
colas@0
   547
    $i = $rev1;
colas@0
   548
    my $tailResult = '';
colas@0
   549
    my $revTitle   = '';
colas@0
   550
    while( $i >= $rev2) {
colas@0
   551
        $revTitle = CGI::a( { href=>$session->getScriptUrl(
colas@0
   552
            0, 'view', $webName, $topic, rev => $i ),
colas@0
   553
                              rel => 'nofollow' },
colas@0
   554
                            $i);
colas@0
   555
        my $revInfo = $session->renderer->renderRevisionInfo( $webName, $topic, undef, $i );
colas@0
   556
        $tailResult .= $tail;
colas@0
   557
        $tailResult =~ s/%REVTITLE%/$revTitle/go;
colas@0
   558
        $tailResult =~ s/%REVINFO%/$revInfo/go;
colas@0
   559
        $i--;
colas@0
   560
    }
colas@0
   561
    $after =~ s/%TAIL%/$tailResult/go;
colas@0
   562
    $after =~ s/%REVISIONS%/$revisions/go;
colas@0
   563
    $after =~ s/%CURRREV%/$rev1/go;
colas@0
   564
    $after =~ s/%MAXREV%/$maxrev/go;
colas@0
   565
colas@0
   566
    $after = $session->handleCommonTags( $after, $webName, $topic );
colas@0
   567
    $after = $session->renderer->getRenderedVersion( $after, $webName, $topic );
colas@0
   568
    $page .= $after;
colas@0
   569
colas@0
   570
    $session->writeCompletePage( $page );
colas@0
   571
}
colas@0
   572
colas@0
   573
1;