Add support for Vary header.
[catagits/Catalyst-View-ContentNegotiation-XHTML.git] / lib / Catalyst / View / ContentNegotiation / XHTML.pm
CommitLineData
e2e866b4 1package Catalyst::View::ContentNegotiation::XHTML;
2
3use Moose::Role;
4use MooseX::Types::Moose qw/Num Str ArrayRef/;
5use MooseX::Types::Structured qw/Tuple/;
6use HTTP::Negotiate qw/choose/;
7
8use namespace::clean -except => 'meta';
9
10# Remember to bump $VERSION in View::TT::XHTML also.
b064d5c4 11our $VERSION = '1.101';
e2e866b4 12
13requires 'process';
14
15has variants => (
16 is => 'ro',
17 isa => ArrayRef[Tuple[Str, Num, Str]],
18 lazy => 1,
19 builder => '_build_variants',
20);
21
22sub _build_variants {
23 return [
24 [qw| xhtml 1.000 application/xhtml+xml |],
25 [qw| html 0.900 text/html |],
26 ];
27}
28
29after process => sub {
30 my ($self, $c) = @_;
8e61c4b2 31 if ( my $accept = $self->pragmatic_accept($c) and $c->response->headers->{'content-type'} =~ m|text/html|) {
32 my $headers = $c->request->headers->clone;
33 $headers->header('Accept' => $accept);
34 if ( choose($self->variants, $headers) eq 'xhtml') {
e2e866b4 35 $c->response->headers->{'content-type'} =~ s|text/html|application/xhtml+xml|;
36 }
37 }
d9925b56 38 $c->response->headers->push_header(Vary => 'Accept');
e2e866b4 39};
40
41sub pragmatic_accept {
42 my ($self, $c) = @_;
8e61c4b2 43 my $accept = $c->request->header('Accept') or return;
e2e866b4 44 if ($accept =~ m|text/html|) {
45 $accept =~ s!\*/\*\s*([,]+|$)!*/*;q=0.5$1!;
8e61c4b2 46 }
47 else {
e2e866b4 48 $accept =~ s!\*/\*\s*([,]+|$)!text/html,*/*;q=0.5$1!;
49 }
8e61c4b2 50 return $accept;
e2e866b4 51}
52
531;
54
55__END__
56
57=head1 NAME
58
84e59a9b 59Catalyst::View::ContentNegotiation::XHTML - Adjusts the response Content-Type
60header to application/xhtml+xml if the browser accepts it.
e2e866b4 61
62=head1 SYNOPSIS
63
64 package Catalyst::View::TT;
65
66 use Moose;
67 use namespace::clean -except => 'meta';
68
69 extends qw/Catalyst::View::TT/;
70 with qw/Catalyst::View::ContentNegotiation::XHTML/;
71
72 1;
73
74=head1 DESCRIPTION
75
84e59a9b 76This is a simple Role which sets the response C<Content-Type> to be
77C<application/xhtml+xml> if the users browser sends an C<Accept> header
e2e866b4 78indicating that it is willing to process that MIME type.
79
84e59a9b 80Changing the C<Content-Type> to C<application/xhtml+xml> causes browsers to
81interpret the page as XML, meaning that your markup must be well formed.
d086c97e 82
83=head1 CAVEATS
e2e866b4 84
84e59a9b 85This is useful when you're developing your application, as you know that all
86pages you view are parsed as XML, so any errors caused by your markup not
87being well-formed will show up at once.
e2e866b4 88
d086c97e 89Whilst this module is has been tested against most popular browsers including
90Internet Explorer, it may cause unexpected results on browsers which do not
91properly support the C<application/xhtml+xml> MIME type.
92
e2e866b4 93=head1 METHOD MODIFIERS
94
95=head2 after process
96
84e59a9b 97Changes the response C<Content-Type> if appropriate (from the requests
98C<Accept> header).
e2e866b4 99
100=head1 METHODS
101
102=head2 pragmatic_accept
103
84e59a9b 104Some browsers (such as Internet Explorer) have a nasty way of sending Accept
105*/* and this claiming to support XHTML just as well as HTML. Saving to a file
106on disk or opening with another application does count as accepting, but it
107really should have a lower q value then text/html. This sub takes a pragmatic
108approach and corrects this mistake by modifying the Accept header before
109passing it to content negotiation.
e2e866b4 110
111=head1 ATTRIBUTES
112
113=head2 variants
114
84e59a9b 115Returns an array ref of 3 part arrays, comprising name, priority, output
e2e866b4 116mime-type, which is used for the content negotiation algorithm.
117
118=head1 PRIVATE METHODS
119
120=head2 _build_variants
121
122Returns the default variant attribute contents.
123
124=head1 SEE ALSO
125
126=over
127
128=item L<Catalyst::View::TT::XHTML> - Trivial Catalyst TT view using this role.
129
84e59a9b 130=item L<http://www.w3.org/Protocols/rfc2616/rfc2616-sec12.html> - Content
131negotiation RFC.
e2e866b4 132
133=back
134
135=head1 BUGS
136
84e59a9b 137Should be split into a base ContentNegotiation role which is consumed by
138ContentNegotiation::XHTML.
e2e866b4 139
140=head1 AUTHOR
141
84e59a9b 142Original author and maintainer - Tomas Doran (t0m)
143C<< <bobtfish@bobtfish.net> >>
e2e866b4 144
145=head1 CONTRIBUTORS
146
147=over
148
84e59a9b 149=item David Dorward - test patches and */* pragmatism to make it work for
150browsers which aren't firefox.
e2e866b4 151
84e59a9b 152=item Florian Ragwitz (rafl) C<< <rafl@debian.org> >> - Conversion into a
153Moose Role, which is what the module should have been originally.
e2e866b4 154
155=back
156
157=head1 COPYRIGHT
158
84e59a9b 159This module itself is copyright (c) 2008 Tomas Doran and is licensed under the
160same terms as Perl itself.
e2e866b4 161
162=cut