]> git.decadent.org.uk Git - maypole.git/blob - lib/Apache/MVC.pm
fixed bug 16869 - forced inheritance of model
[maypole.git] / lib / Apache / MVC.pm
1 package Apache::MVC;
2
3 our $VERSION = '2.11';
4
5 use strict;
6 use warnings;
7
8 use base 'Maypole';
9 use Maypole::Headers;
10 use Maypole::Constants;
11
12 __PACKAGE__->mk_accessors( qw( ar ) );
13
14 our $MODPERL2;
15 our $modperl_version;
16
17 BEGIN {
18     eval 'use Apache;';
19     if ($@) {
20          eval 'use mod_perl2; $modperl_version = $mod_perl2::VERSION; ';
21          if ($@) {
22            $modperl_version = $Apache2::RequestRec::VERSION;
23          }
24          require Apache2::RequestIO;
25          require Apache2::RequestRec;
26          require Apache2::RequestUtil;
27          require APR::URI;
28          require HTTP::Body;
29          $MODPERL2 = 1;
30     } else {
31         eval ' use mod_perl; ';
32         require Apache;
33         require Apache::Request;
34         $MODPERL2 = 0;
35         $modperl_version = 1;
36     }
37
38 }
39
40 =head1 NAME
41
42 Apache::MVC - Apache front-end to Maypole
43
44 =head1 SYNOPSIS
45
46     package BeerDB;
47     use Maypole::Application;
48
49 =head1 DESCRIPTION
50
51 A mod_perl platform driver for Maypole. Your application can inherit from
52 Apache::MVC directly, but it is recommended that you use
53 L<Maypole::Application>.
54
55 =head1 INSTALLATION
56
57 Create a driver module like the one illustrated in L<Maypole::Application>.
58
59 Put the following in your Apache config:
60
61     <Location /beer>
62         SetHandler perl-script
63         PerlHandler BeerDB
64     </Location>
65
66 Copy the templates found in F<templates/factory> into the F<beer/factory>
67 directory off the web root. When the designers get back to you with custom
68 templates, they are to go in F<beer/custom>. If you need to override templates
69 on a database-table-by-table basis, put the new template in F<beer/I<table>>.
70
71 This will automatically give you C<add>, C<edit>, C<list>, C<view> and C<delete>
72 commands; for instance, to see a list of breweries, go to
73
74     http://your.site/beer/brewery/list
75
76 For more information about how the system works and how to extend it,
77 see L<Maypole>.
78
79 =head1 Implementation
80
81 This class overrides a set of methods in the base Maypole class to provide its
82 functionality. See L<Maypole> for these:
83
84 =over
85
86 =item get_request
87
88 =cut
89
90 sub get_request {
91     my ($self, $r) = @_;
92     my $ar = ($MODPERL2) ? $r : Apache::Request->instance($r);
93     $self->ar($ar);
94 }
95
96 =item parse_location
97
98 =cut
99
100 sub parse_location {
101     my $self = shift;
102
103     # Reconstruct the request headers
104     $self->headers_in(Maypole::Headers->new);
105     my %headers;
106     if ($MODPERL2) { %headers = %{$self->ar->headers_in};
107     } else { %headers = $self->ar->headers_in; }
108     for (keys %headers) {
109         $self->headers_in->set($_, $headers{$_});
110     }
111     my $path = $self->ar->uri;
112     my $loc  = $self->ar->location;
113     {
114         no warnings 'uninitialized';
115         $path .= '/' if $path eq $loc;
116         $path =~ s/^($loc)?\///;
117     }
118     $self->path($path);
119     
120     $self->parse_path;
121     $self->parse_args;
122 }
123
124 =item parse_args
125
126 =cut
127
128 sub parse_args {
129     my $self = shift;
130     $self->params( { $self->_mod_perl_args( $self->ar ) } );
131     $self->query( $self->params );
132 }
133
134 =item redirect_request
135
136 =cut
137
138 # FIXME: use headers_in to gather host and other information?
139 sub redirect_request 
140 {
141   my $r = shift;
142   my $redirect_url = $_[0];
143   my $status = "302";
144   if ($_[1]) {
145     my %args = @_;
146     if ($args{url}) {
147       $redirect_url = $args{url};
148     } else {
149       my $path = $args{path} || $r->path;
150       my $host = $args{domain} || $r->ar->hostname;
151       my $protocol = $args{protocol} || $r->get_protocol;
152       $redirect_url = "${protocol}://${host}/${path}";
153     }
154     $status = $args{status} if ($args{status});
155   }
156
157   $r->headers_out->set('Status' => $status);
158   $r->headers_out->set('Location' => $redirect_url);
159   return OK;
160 }
161
162 =item get_protocol
163
164 =cut
165
166 sub get_protocol {
167   my $self = shift;
168   my $protocol = ( $self->ar->protocol =~ m/https/i ) ? 'https' : 'http' ;
169   return $protocol;
170 }
171
172 =item send_output
173
174 =cut
175
176 sub send_output {
177     my $r = shift;
178     $r->ar->content_type(
179           $r->content_type =~ m/^text/
180         ? $r->content_type . "; charset=" . $r->document_encoding
181         : $r->content_type
182     );
183     $r->ar->headers_out->set(
184         "Content-Length" => do { use bytes; length $r->output }
185     );
186
187     foreach ($r->headers_out->field_names) {
188         next if /^Content-(Type|Length)/;
189         $r->ar->headers_out->set($_ => $r->headers_out->get($_));
190     }
191
192     $MODPERL2 || $r->ar->send_http_header;
193     $r->ar->print( $r->output );
194 }
195
196 =item get_template_root
197
198 =cut
199
200 sub get_template_root {
201     my $r = shift;
202     $r->ar->document_root . "/" . $r->ar->location;
203 }
204
205 =back
206
207 =cut
208
209 #########################################################
210 # private / internal methods and subs
211
212
213 sub _mod_perl_args {
214     my ( $self, $apr ) = @_;
215     my %args;
216     if ($apr->isa('Apache::Request')) {
217       foreach my $key ( $apr->param ) {
218         my @values = $apr->param($key);
219         $args{$key} = @values == 1 ? $values[0] : \@values;
220       }
221     } else {
222       my $body = $self->_prepare_body($apr);
223       %args = %{$body->param};
224     }
225     return %args;
226 }
227
228 sub _prepare_body {
229     my ( $self, $r ) = @_;
230
231     unless ($self->{__http_body}) {
232         my $content_type   = $r->headers_in->get('Content-Type');
233         my $content_length = $r->headers_in->get('Content-Length');
234         my $body   = HTTP::Body->new( $content_type, $content_length );
235         my $length = $content_length;
236         while ( $length ) {
237             $r->read( my $buffer, ( $length < 8192 ) ? $length : 8192 );
238             $length -= length($buffer);
239             $body->add($buffer);
240         }
241         $self->{__http_body} = $body;
242     }
243     return $self->{__http_body};
244 }
245
246
247
248 =head1 AUTHOR
249
250 Simon Cozens, C<simon@cpan.org>
251
252 =head1 CREDITS
253
254 Aaron Trevena
255 Marcus Ramberg, C<marcus@thefeed.no>
256 Sebastian Riedel, C<sri@oook.de>
257
258 =head1 LICENSE
259
260 You may distribute this code under the same terms as Perl itself.
261
262 =cut
263
264 1;