File Coverage

blib/lib/Catalyst/TraitFor/View/MarkupValidation.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package Catalyst::TraitFor::View::MarkupValidation;
2              
3 1     1   981 use strict;
  1         3  
  1         42  
4 1     1   7 use warnings;
  1         2  
  1         62  
5 1     1   19 use Carp;
  1         2  
  1         81  
6 1     1   510 use Moose::Role;
  0            
  0            
7             use Template;
8             use WebService::Validator::HTML::W3C;
9             use Syntax::Highlight::Engine::Kate;
10             use namespace::autoclean;
11             use Memoize;
12              
13             our $VERSION = '0.003';
14              
15             after process => sub {
16             my ( $self, $c ) = @_;
17              
18             # Only try to validate when in debug mode, and only handle HTML documents (unless overridden (TODO!))
19             if ( ( !$c->debug )
20             || $c->res->header('Content-type') !~
21             m{(text/html|application/xhtml+xml)}mxs )
22             {
23             return;
24             }
25            
26             my $source = $c->res->body;
27             my $validator_uri = $c->config->{MARKUP_VALIDATOR_URI};
28             my @report = _validate($source, $validator_uri);
29            
30             # continue as normal if no errors
31             if (!scalar @report) {
32             return;
33             }
34              
35             my $template_html = q[<!doctype html>
36             <html>
37             <head>
38             <title>Error report</title>
39             <style type="text/css">
40             .DataType { color: red; }
41             .Normal { color: black; }
42             .Keyword { font-weight: bold; }
43             .String { font-style: italic; }
44             .Others { color: blue; }
45             </style>
46             </head>
47             <body>
48             <h1>Error report</h1>
49            
50             <table>
51             <tr>
52             <th scope="col">Line</th>
53             <th scope="col">Col</th>
54             <th scope="col">Error</th>
55             </tr>
56             [% FOREACH error = report %]
57             <tr>
58             <td>[% error.0 %]</td>
59             <td>[% error.1 %]</td>
60             <td>[% error.2 %]</td>
61             </tr>
62             [% END %]
63             </table>
64             <pre>[% source %]</pre>
65             </body>
66             </html>
67             ]; #$c->config->{MARKUP_VALIDATOR_REPORT_TEMPLATE} || \*DATA;
68              
69             my $hl = Syntax::Highlight::Engine::Kate->new(
70             language => 'HTML',
71             substitutions => {
72             q[<] => q[&lt;],
73             q[>] => q[&gt;],
74             q[&] => q[&amp;],
75             },
76             format_table => {
77             # convert Kate's internal representation into
78             # <code class="<internal name>"> value </code>
79             map { $_ => [ qq{<code class="$_">}, '</code>' ] }
80             qw/Alert BaseN BString Char Comment DataType
81             DecVal Error Float Function IString Keyword
82             Normal Operator Others RegionMarker Reserved
83             String Variable Warning/,
84             },
85             );
86              
87             my $highlighted_source = $hl->highlightText($source);
88              
89             my $data_for_tt = {
90             source => $highlighted_source,
91             report => \@report
92             };
93             my $template = Template->new();
94             my $output;
95             $template->process( \$template_html, $data_for_tt, \$output ) or croak ("The template didn't work! ", $!);
96             $c->res->body($output);
97             };
98              
99             sub _validate {
100             my ($source, $validator_uri) = @_;
101            
102             if (!$validator_uri) {
103             carp "MARKUP_VALIDATOR_URI has not been configured. Will skip Catalyst::TraitFor::View::MarkupValidation";
104             return;
105             }
106            
107             my $v = WebService::Validator::HTML::W3C->new(
108             detailed => 1,
109             validator_uri => $validator_uri
110             );
111              
112             # Perform the validation
113             $v->validate( string => $source ) or croak($!);
114              
115             # Don't switch to error reporting unless there are errors
116             if ( $v->is_valid ) {
117             return;
118             }
119              
120             my $errors = $v->errors();
121             my @report = ();
122             foreach my $err ( @{$errors} ) {
123             push @report, [ $err->line, $err->col, $err->msg ];
124             }
125            
126             return @report;
127             }
128              
129             memoize('_validate');
130              
131             1;
132              
133             =head1 NAME
134              
135             Catalyst::TraitFor::View::MarkupValidation - Validates output and replaces it with an error report if not OK
136              
137             =head1 SYNOPSIS
138              
139             package Catalyst::View::Validation;
140              
141             use Moose;
142             use namespace::autoclean;
143              
144             extends qw/Catalyst::View::TT/;
145             with qw/Catalyst::TraitFor::View::MarkupValidation/;
146              
147             __PACKAGE__->config(MARKUP_VALIDATOR_URI => q[http://localhost/w3c-validator/check]);
148              
149             1;
150              
151             =head1 DESCRIPTION
152              
153             This is a Role which which takes generated content that is ready for output and
154             validates it. If there are errors it replaces the default output with an
155             error report.
156              
157             =head1 CONFIGURATION
158              
159             You must set the MARKUP_VALIDATOR_URI to the URI for an instance of the W3C
160             Markup Validation Service. You are encouraged to install a local instance of
161             the validator so that you see high levels of performance and do not hammer the
162             public server (which is funded by donations).
163              
164             See L<http://validator.w3.org/source/> for downloads and installation instructions
165             on a variety of platforms.
166              
167             =head1 CAVEATS
168              
169             This is useful when you're developing your application, as it will identify
170             validity errors in the markup. In production, however, the performance cost is
171             likely to be too high, and throwing errors at users that browsers could
172             probably recover from is unfriendly.
173              
174             This module checks that Catalyst is running in debug mode and will not run if
175             it is not.
176              
177             =head1 METHOD MODIFIERS
178              
179             =head2 after process
180              
181             Validates document and (in event of an error) replaces it with an error report.
182              
183             =head1 TODO
184              
185             =over
186              
187             =item Make document types that get validated configurable
188              
189             =item Add line numbering to output
190              
191             =item Hyperlink from error to source
192              
193             =back
194              
195             =head1 BUGS AND LIMITATIONS
196              
197             Please report any you find using RT.
198              
199             =over
200              
201             =item If URI to validation service is incorrect, shows error report w/ 0 errors.
202              
203             =back
204              
205             =head1 AUTHOR
206              
207             =over
208              
209             =item David Dorward (dorward) C<< <david@dorward.me.uk> >>
210              
211             =back
212              
213             =head1 CONTRIBUTORS
214              
215             =over
216              
217             =item Tomas Doran (t0m) C<< <bobtfish@bobtfish.net> >>
218              
219             =back
220              
221             =head1 LICENSE AND COPYRIGHT
222              
223             This module itself is copyright (c) 2009 David Dorward and is licensed under the
224             same terms as Perl itself.
225              
226             =cut
227              
228             __DATA__
229             <!doctype html>
230             <html>
231             <head>
232             <title>Error report</title>
233             <style type="text/css">
234             .DataType { color: red; }
235             .Normal { color: black; }
236             .Keyword { font-weight: bold; }
237             .String { font-style: italic; }
238             .Others { color: blue; }
239             </style>
240             </head>
241             <body>
242             <h1>Error report</h1>
243            
244             <table>
245             <tr>
246             <th scope="col">Line</th>
247             <th scope="col">Col</th>
248             <th scope="col">Error</th>
249             </tr>
250             [% FOREACH error = report %]
251             <tr>
252             <td>[% error.0 %]</td>
253             <td>[% error.1 %]</td>
254             <td>[% error.2 %]</td>
255             </tr>
256             [% END %]
257             </table>
258             <pre>[% source %]</pre>
259             </body>
260             </html>