SVN::Notify::HTML::ColorDiff - Subversion activity HTML notification with colorized diff


SVN-Notify documentation Contained in the SVN-Notify distribution.

Index


Code Index:

Name

Top

SVN::Notify::HTML::ColorDiff - Subversion activity HTML notification with colorized diff

Synopsis

Top

Use svnnotify in post-commit:

  svnnotify --repos-path "$1" --revision "$2" \
    --to developers@example.com --handler HTML::ColorDiff [options]

Use the class in a custom script:

  use SVN::Notify::HTML::ColorDiff;

  my $notifier = SVN::Notify::HTML::ColorDiff->new(%params);
  $notifier->prepare;
  $notifier->execute;

Description

Top

This subclass of SVN::Notify::HTML sends HTML formatted email messages for Subversion activity, and if the with_diff parameter is specified (but not attach_diff), then a pretty colorized version of the diff will be included, rather than the plain text diff output by SVN::Notify::HTML.

Usage

Top

To use SVN::Notify::HTML::ColorDiff, simply follow the instructions in SVN::Notify, but when using svnnotify, specify --handler HTML::ColorDiff.

Instance Interface

Top

Instance Methods

output_css

  $notifier->output_css($file_handle);

This method starts outputs the CSS for the HTML message. SVN::Notify::HTML::ColorDiff adds extra CSS to its output so that it can nicely style the diff.

output_diff

  $notifier->output_diff($out_file_handle, $diff_file_handle);

Reads the diff data from $diff_file_handle and prints it to $out_file_handle for inclusion in the notification message. The diff is output with nice colorized HTML markup. Each line of the diff file is escaped by HTML::Entities::encode_entities().

If there are any diff filters, this method will do no HTML formatting, but redispatch to SVN::Notify::output_diff. See Writing Output Filters for details on filters.

See Also

Top

SVN::Notify
SVN::Notify::HTML
CVSspam

To Do

Top

Top

Author

Top

David E. Wheeler <david@justatheory.com>

Copyright and License

Top


SVN-Notify documentation Contained in the SVN-Notify distribution.
package SVN::Notify::HTML::ColorDiff;

use strict;
use HTML::Entities;
use SVN::Notify::HTML ();

$SVN::Notify::HTML::ColorDiff::VERSION = '2.82';
@SVN::Notify::HTML::ColorDiff::ISA = qw(SVN::Notify::HTML);

##############################################################################

# We use _css() so that ColorDiff can override it and the filters then applied
# only one to all of the CSS.

##############################################################################

my %types = (
    Modified => 'modfile',
    Added    => 'addfile',
    Deleted  => 'delfile',
    Copied   => 'copfile',
);

sub output_diff {
    my ($self, $out, $diff) = @_;
    if ( $self->filters_for('diff') ) {
        return $self->SUPER::output_diff($out, $diff);
    }
    $self->_dbpnt( "Outputting colorized HTML diff") if $self->verbose > 1;

    my $in_div;
    my $in_span = '';
    print $out qq{</div>\n<div id="patch">\n<h3>Diff</h3>\n};
    my ($length, %seen) = 0;
    my $max = $self->max_diff_length;

    while (my $line = <$diff>) {
        $line =~ s/[\n\r]+$//;
        next unless $line;
        if ( $max && ( $length += length $line ) >= $max ) {
            print $out "</$in_span>" if $in_span;
            print $out qq{<span class="lines">\@\@ Diff output truncated at $max characters. \@\@\n</span>};
            $in_span = '';
            last;
        } else {
            if ($line =~ /^(Modified|Added|Deleted|Copied): (.*)/) {
                my $class = $types{my $action = $1};
                ++$seen{$2};
                my $file = encode_entities($2, '<>&"');
                (my $id = $file) =~ s/[^\w_]//g;

                print $out "</$in_span>" if $in_span;
                print $out "</span></pre></div>\n" if $in_div;

                # Dump line, but check it's content.
                if (<$diff> !~ /^=/) {
                    # Looks like they used --no-diff-added or --no-diff-deleted.
                    ($in_span, $in_div) = '';
                    print $out qq{<a id="$id"></a>\n<div class="$class">},
                        qq{<h4>$action: $file</h4></div>\n};
                    next;
                }

                # Get the revision numbers.
                my $before = <$diff>;
                $before =~ s/[\n\r]+$//;

                if ($before =~ /^\(Binary files differ\)/) {
                    # Just output the whole file div.
                    print $out qq{<a id="$id"></a>\n<div class="binary"><h4>},
                      qq{$action: $file</h4>\n<pre class="diff"><span>\n},
                      qq{<span class="cx">$before\n</span></span></pre></div>\n};
                    ($in_span, $in_div) = '';
                    next;
                }

                my ($rev1) = $before =~ /\(rev (\d+)\)$/;
                my $after = <$diff>;
                $after =~ s/[\n\r]+$//;
                my ($rev2) = $after =~ /\(rev (\d+)\)$/;

                # Output the headers.
                print $out qq{<a id="$id"></a>\n<div class="$class"><h4>$action: $file},
                  " ($rev1 => $rev2)</h4>\n";
                print $out qq{<pre class="diff"><span>\n<span class="info">};
                $in_div = 1;
                print $out encode_entities($_, '<>&"'), "\n" for ($before, $after);
                print $out "</span>";
                $in_span = '';
            } elsif ($line =~ /^Property changes on: (.*)/ && !$seen{$1}) {
                # It's just property changes.
                my $file = encode_entities($1, '<>&"');
                (my $id = $file) =~ s/[^\w_]//g;
                # Dump line.
                <$diff>;

                # Output the headers.
                print $out "</$in_span>" if $in_span;
                print $out "</span></pre></div>\n" if $in_div;
                print $out qq{<a id="$id"></a>\n<div class="propset">},
                  qq{<h4>Property changes: $file</h4>\n<pre class="diff"><span>\n};
                $in_div = 1;
                $in_span = '';
            } elsif ($line =~ /^\@\@/) {
                print $out "</$in_span>" if $in_span;
                print $out (
                    qq{<span class="lines">},
                    encode_entities($line, '<>&"'),
                    "\n</span>",
                );
                $in_span = '';
            } elsif ($line =~ /^([-+])/) {
                my $type = $1 eq '+' ? 'ins' : 'del';
                if ($in_span eq $type) {
                    print $out encode_entities($line, '<>&"'), "\n";
                } else {
                    print $out "</$in_span>" if $in_span;
                    print $out (
                        qq{<$type>},
                        encode_entities($line, '<>&"'),
                        "\n",
                    );
                    $in_span = $type;
                }
            } else {
                if ($in_span eq 'cx') {
                    print $out encode_entities($line, '<>&"'), "\n";
                } else {
                    print $out "</$in_span>" if $in_span;
                    print $out (
                        qq{<span class="cx">},
                        encode_entities($line, '<>&"'),
                        "\n",
                    );
                    $in_span = 'span';
                }
            }
        }
    }
    print $out "</$in_span>" if $in_span;
    print $out "</span></pre>\n</div>\n" if $in_div;
    print $out "</div>\n";

    close $diff or warn "Child process exited: $?\n";
    return $self;
}

##############################################################################

sub _css {
    my $css = shift->SUPER::_css;
    push @$css,
        qq(#patch h4 {font-family: verdana,arial,helvetica,sans-serif;),
            qq(font-size:10pt;padding:8px;background:#369;color:#fff;),
            qq(margin:0;}\n),
        qq(#patch .propset h4, #patch .binary h4 {margin:0;}\n),
         qq(#patch pre {padding:0;line-height:1.2em;margin:0;}\n),
        qq(#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;),
            qq(overflow:auto;}\n),
        qq(#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}\n),
        qq(#patch span {display:block;padding:0 10px;}\n),
        qq(#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, ),
            qq(#patch .binary, #patch .copfile {border:1px solid #ccc;),
            qq(margin:10px 0;}\n),
        qq(#patch ins {background:#dfd;text-decoration:none;display:block;),
            qq(padding:0 10px;}\n),
        qq(#patch del {background:#fdd;text-decoration:none;display:block;),
            qq(padding:0 10px;}\n),
        qq(#patch .lines, .info {color:#888;background:#fff;}\n);
    return $css;
}

1;
__END__