| blib/lib/SVN/Notify/HTML/ColorDiff.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 82 | 83 | 98.8 |
| branch | 44 | 48 | 91.6 |
| condition | 6 | 6 | 100.0 |
| subroutine | 5 | 5 | 100.0 |
| pod | 1 | 1 | 100.0 |
| total | 138 | 143 | 96.5 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package SVN::Notify::HTML::ColorDiff; | ||||||
| 2 | |||||||
| 3 | 134 | 134 | 546544 | use strict; | |||
| 134 | 477 | ||||||
| 134 | 4779 | ||||||
| 4 | 134 | 134 | 932 | use HTML::Entities; | |||
| 134 | 337 | ||||||
| 134 | 9486 | ||||||
| 5 | 134 | 134 | 28566 | use SVN::Notify::HTML (); | |||
| 134 | 401 | ||||||
| 134 | 175110 | ||||||
| 6 | |||||||
| 7 | $SVN::Notify::HTML::ColorDiff::VERSION = '2.87'; | ||||||
| 8 | @SVN::Notify::HTML::ColorDiff::ISA = qw(SVN::Notify::HTML); | ||||||
| 9 | |||||||
| 10 | =head1 Name | ||||||
| 11 | |||||||
| 12 | SVN::Notify::HTML::ColorDiff - Subversion activity HTML notification with colorized diff | ||||||
| 13 | |||||||
| 14 | =head1 Synopsis | ||||||
| 15 | |||||||
| 16 | Use F |
||||||
| 17 | |||||||
| 18 | svnnotify --repos-path "$1" --revision "$2" \ | ||||||
| 19 | --to developers@example.com --handler HTML::ColorDiff [options] | ||||||
| 20 | |||||||
| 21 | Use the class in a custom script: | ||||||
| 22 | |||||||
| 23 | use SVN::Notify::HTML::ColorDiff; | ||||||
| 24 | |||||||
| 25 | my $notifier = SVN::Notify::HTML::ColorDiff->new(%params); | ||||||
| 26 | $notifier->prepare; | ||||||
| 27 | $notifier->execute; | ||||||
| 28 | |||||||
| 29 | =head1 Description | ||||||
| 30 | |||||||
| 31 | This subclass of L |
||||||
| 32 | email messages for Subversion activity, and if the C |
||||||
| 33 | specified (but not C |
||||||
| 34 | diff will be included, rather than the plain text diff output by | ||||||
| 35 | SVN::Notify::HTML. | ||||||
| 36 | |||||||
| 37 | =head1 Usage | ||||||
| 38 | |||||||
| 39 | To use SVN::Notify::HTML::ColorDiff, simply follow the | ||||||
| 40 | L |
||||||
| 41 | specify C<--handler HTML::ColorDiff>. | ||||||
| 42 | |||||||
| 43 | =cut | ||||||
| 44 | |||||||
| 45 | ############################################################################## | ||||||
| 46 | |||||||
| 47 | =head1 Instance Interface | ||||||
| 48 | |||||||
| 49 | =head2 Instance Methods | ||||||
| 50 | |||||||
| 51 | =head3 output_css | ||||||
| 52 | |||||||
| 53 | $notifier->output_css($file_handle); | ||||||
| 54 | |||||||
| 55 | This method starts outputs the CSS for the HTML message. | ||||||
| 56 | SVN::Notify::HTML::ColorDiff adds extra CSS to its output so that it can | ||||||
| 57 | nicely style the diff. | ||||||
| 58 | |||||||
| 59 | =cut | ||||||
| 60 | |||||||
| 61 | # We use _css() so that ColorDiff can override it and the filters then applied | ||||||
| 62 | # only one to all of the CSS. | ||||||
| 63 | |||||||
| 64 | ############################################################################## | ||||||
| 65 | |||||||
| 66 | =head3 output_diff | ||||||
| 67 | |||||||
| 68 | $notifier->output_diff($out_file_handle, $diff_file_handle); | ||||||
| 69 | |||||||
| 70 | Reads the diff data from C<$diff_file_handle> and prints it to | ||||||
| 71 | C<$out_file_handle> for inclusion in the notification message. The diff is | ||||||
| 72 | output with nice colorized HTML markup. Each line of the diff file is escaped | ||||||
| 73 | by C |
||||||
| 74 | |||||||
| 75 | If there are any C |
||||||
| 76 | redispatch to L |
||||||
| 77 | L |
||||||
| 78 | filters. | ||||||
| 79 | |||||||
| 80 | =cut | ||||||
| 81 | |||||||
| 82 | my %types = ( | ||||||
| 83 | Modified => 'modfile', | ||||||
| 84 | Added => 'addfile', | ||||||
| 85 | Deleted => 'delfile', | ||||||
| 86 | Copied => 'copfile', | ||||||
| 87 | ); | ||||||
| 88 | |||||||
| 89 | sub output_diff { | ||||||
| 90 | 145 | 145 | 1 | 3817 | my ($self, $out, $diff) = @_; | ||
| 91 | 145 | 100 | 2477 | if ( $self->filters_for('diff') ) { | |||
| 92 | 14 | 1218 | return $self->SUPER::output_diff($out, $diff); | ||||
| 93 | } | ||||||
| 94 | 131 | 50 | 3067 | $self->_dbpnt( "Outputting colorized HTML diff") if $self->verbose > 1; | |||
| 95 | |||||||
| 96 | 131 | 358 | my $in_div; | ||||
| 97 | 131 | 1380 | my $in_span = ''; | ||||
| 98 | 131 | 2095 | print $out qq{\n \n Diff\n}; |
||||
| 99 | 131 | 629 | my ($length, %seen) = 0; | ||||
| 100 | 131 | 3981 | my $max = $self->max_diff_length; | ||||
| 101 | |||||||
| 102 | 131 | 92658148 | while (my $line = <$diff>) { | ||||
| 103 | 3271 | 30375 | $line =~ s/[\n\r]+$//; | ||||
| 104 | 3271 | 100 | 8206 | next unless $line; | |||
| 105 | 3054 | 100 | 100 | 7870 | if ( $max && ( $length += length $line ) >= $max ) { | ||
| 106 | 8 | 50 | 232 | print $out "$in_span>" if $in_span; | |||
| 107 | 8 | 152 | print $out qq{\@\@ Diff output truncated at $max characters. \@\@\n}; | ||||
| 108 | 8 | 72 | $in_span = ''; | ||||
| 109 | 8 | 96 | last; | ||||
| 110 | } else { | ||||||
| 111 | 3046 | 100 | 100 | 22014 | if ($line =~ /^(Modified|Added|Deleted|Copied): (.*)/) { | ||
| 100 | |||||||
| 100 | |||||||
| 100 | |||||||
| 112 | 250 | 3110 | my $class = $types{my $action = $1}; | ||||
| 113 | 250 | 3911 | ++$seen{$2}; | ||||
| 114 | 250 | 2824 | my $file = encode_entities($2, '<>&"'); | ||||
| 115 | 250 | 23992 | (my $id = $file) =~ s/[^\w_]//g; | ||||
| 116 | |||||||
| 117 | 250 | 100 | 1989 | print $out "$in_span>" if $in_span; | |||
| 118 | 250 | 100 | 1102 | print $out "\n" if $in_div; | |||
| 119 | |||||||
| 120 | # Dump line, but check it's content. | ||||||
| 121 | 250 | 100 | 3151 | if (<$diff> !~ /^=/) { | |||
| 122 | # Looks like they used --no-diff-added or --no-diff-deleted. | ||||||
| 123 | 35 | 700 | ($in_span, $in_div) = ''; | ||||
| 124 | 35 | 700 | print $out qq{\n }, |
||||
| 125 | qq{$action: $file\n}; |
||||||
| 126 | 35 | 840 | next; | ||||
| 127 | } | ||||||
| 128 | |||||||
| 129 | # Get the revision numbers. | ||||||
| 130 | 215 | 1268 | my $before = <$diff>; | ||||
| 131 | 215 | 2342 | $before =~ s/[\n\r]+$//; | ||||
| 132 | |||||||
| 133 | 215 | 100 | 1322 | if ($before =~ /^\(Binary files differ\)/) { | |||
| 134 | # Just output the whole file div. | ||||||
| 135 | 1 | 25 | print $out qq{\n}, |
||||
| 136 | qq{$action: $file\n\n}, |
||||||
| 137 | qq{$before\n\n}; | ||||||
| 138 | 1 | 9 | ($in_span, $in_div) = ''; | ||||
| 139 | 1 | 10 | next; | ||||
| 140 | } | ||||||
| 141 | |||||||
| 142 | 214 | 2291 | my ($rev1) = $before =~ /\(rev (\d+)\)$/; | ||||
| 143 | 214 | 958 | my $after = <$diff>; | ||||
| 144 | 214 | 2097 | $after =~ s/[\n\r]+$//; | ||||
| 145 | 214 | 2298 | my ($rev2) = $after =~ /\(rev (\d+)\)$/; | ||||
| 146 | |||||||
| 147 | # Output the headers. | ||||||
| 148 | 214 | 4517 | print $out qq{\n$action: $file}, |
||||
| 149 | " ($rev1 => $rev2)\n"; | ||||||
| 150 | 214 | 4779 | print $out qq{\n}; |
||||
| 151 | 214 | 663 | $in_div = 1; | ||||
| 152 | 214 | 1591 | print $out encode_entities($_, '<>&"'), "\n" for ($before, $after); | ||||
| 153 | 214 | 17054 | print $out ""; | ||||
| 154 | 214 | 1620 | $in_span = ''; | ||||
| 155 | } elsif ($line =~ /^Property changes on: (.*)/ && !$seen{$1}) { | ||||||
| 156 | # It's just property changes. | ||||||
| 157 | 31 | 618 | my $file = encode_entities($1, '<>&"'); | ||||
| 158 | 31 | 1851 | (my $id = $file) =~ s/[^\w_]//g; | ||||
| 159 | # Dump line. | ||||||
| 160 | 31 | 364 | <$diff>; | ||||
| 161 | |||||||
| 162 | # Output the headers. | ||||||
| 163 | 31 | 100 | 513 | print $out "$in_span>" if $in_span; | |||
| 164 | 31 | 100 | 408 | print $out "\n" if $in_div; | |||
| 165 | 31 | 431 | print $out qq{\n }, |
||||
| 166 | qq{Property changes: $file\n\n}; |
||||||
| 167 | 31 | 198 | $in_div = 1; | ||||
| 168 | 31 | 273 | $in_span = ''; | ||||
| 169 | } elsif ($line =~ /^\@\@/) { | ||||||
| 170 | 300 | 100 | 1321 | print $out "$in_span>" if $in_span; | |||
| 171 | 300 | 1389 | print $out ( | ||||
| 172 | qq{}, | ||||||
| 173 | encode_entities($line, '<>&"'), | ||||||
| 174 | "\n", | ||||||
| 175 | ); | ||||||
| 176 | 300 | 12077 | $in_span = ''; | ||||
| 177 | } elsif ($line =~ /^([-+])/) { | ||||||
| 178 | 576 | 100 | 1883 | my $type = $1 eq '+' ? 'ins' : 'del'; | |||
| 179 | 576 | 100 | 1407 | if ($in_span eq $type) { | |||
| 180 | 170 | 4777 | print $out encode_entities($line, '<>&"'), "\n"; | ||||
| 181 | } else { | ||||||
| 182 | 406 | 100 | 2151 | print $out "$in_span>" if $in_span; | |||
| 183 | 406 | 1961 | print $out ( | ||||
| 184 | qq{<$type>}, | ||||||
| 185 | encode_entities($line, '<>&"'), | ||||||
| 186 | "\n", | ||||||
| 187 | ); | ||||||
| 188 | 406 | 17006 | $in_span = $type; | ||||
| 189 | } | ||||||
| 190 | } else { | ||||||
| 191 | 1889 | 50 | 4610 | if ($in_span eq 'cx') { | |||
| 192 | 0 | 0 | print $out encode_entities($line, '<>&"'), "\n"; | ||||
| 193 | } else { | ||||||
| 194 | 1889 | 100 | 6702 | print $out "$in_span>" if $in_span; | |||
| 195 | 1889 | 6025 | print $out ( | ||||
| 196 | qq{}, | ||||||
| 197 | encode_entities($line, '<>&"'), | ||||||
| 198 | "\n", | ||||||
| 199 | ); | ||||||
| 200 | 1889 | 79309 | $in_span = 'span'; | ||||
| 201 | } | ||||||
| 202 | } | ||||||
| 203 | } | ||||||
| 204 | } | ||||||
| 205 | 131 | 100 | 2835 | print $out "$in_span>" if $in_span; | |||
| 206 | 131 | 100 | 1012 | print $out "\n\n" if $in_div; | |||
| 207 | 131 | 544 | print $out "\n"; | ||||
| 208 | |||||||
| 209 | 131 | 50 | 7770 | close $diff or warn "Child process exited: $?\n"; | |||
| 210 | 131 | 1122 | return $self; | ||||
| 211 | } | ||||||
| 212 | |||||||
| 213 | ############################################################################## | ||||||
| 214 | |||||||
| 215 | sub _css { | ||||||
| 216 | 394 | 394 | 7254 | my $css = shift->SUPER::_css; | |||
| 217 | 394 | 8428 | push @$css, | ||||
| 218 | qq(#patch h4 {font-family: verdana,arial,helvetica,sans-serif;), | ||||||
| 219 | qq(font-size:10pt;padding:8px;background:#369;color:#fff;), | ||||||
| 220 | qq(margin:0;}\n), | ||||||
| 221 | qq(#patch .propset h4, #patch .binary h4 {margin:0;}\n), | ||||||
| 222 | qq(#patch pre {padding:0;line-height:1.2em;margin:0;}\n), | ||||||
| 223 | qq(#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;), | ||||||
| 224 | qq(overflow:auto;}\n), | ||||||
| 225 | qq(#patch .propset .diff, #patch .binary .diff {padding:10px 0;}\n), | ||||||
| 226 | qq(#patch span {display:block;padding:0 10px;}\n), | ||||||
| 227 | qq(#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, ), | ||||||
| 228 | qq(#patch .binary, #patch .copfile {border:1px solid #ccc;), | ||||||
| 229 | qq(margin:10px 0;}\n), | ||||||
| 230 | qq(#patch ins {background:#dfd;text-decoration:none;display:block;), | ||||||
| 231 | qq(padding:0 10px;}\n), | ||||||
| 232 | qq(#patch del {background:#fdd;text-decoration:none;display:block;), | ||||||
| 233 | qq(padding:0 10px;}\n), | ||||||
| 234 | qq(#patch .lines, .info {color:#888;background:#fff;}\n); | ||||||
| 235 | 394 | 3893 | return $css; | ||||
| 236 | } | ||||||
| 237 | |||||||
| 238 | 1; | ||||||
| 239 | __END__ |