]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole.pm
Rearranged pod for new methods
[maypole.git] / lib / Maypole.pm
1 package Maypole;
2 use base qw(Class::Accessor::Fast Class::Data::Inheritable);
3 use UNIVERSAL::require;
4 use strict;
5 use warnings;
6 use Maypole::Config;
7 use Maypole::Constants;
8 use Maypole::Headers;
9 use URI();
10
11 our $VERSION = '2.11';
12
13 # proposed privacy conventions:
14 # - no leading underscore     - public to custom application code and plugins
15 # - single leading underscore - private to the main Maypole stack - *not*
16 #     including plugins
17 # - double leading underscore - private to the current package
18
19 __PACKAGE__->mk_classdata($_) for qw( config init_done view_object );
20 __PACKAGE__->mk_accessors(
21     qw( params query objects model_class template_args output path
22         args action template error document_encoding content_type table
23         headers_in headers_out stash)
24 );
25 __PACKAGE__->config( Maypole::Config->new() );
26 __PACKAGE__->init_done(0);
27
28 sub debug { 0 }
29
30 sub setup 
31 {
32     my $calling_class = shift;
33     
34     $calling_class = ref $calling_class if ref $calling_class;
35     
36     my $config = $calling_class->config;
37     
38     $config->model || $config->model('Maypole::Model::CDBI');
39     
40     $config->model->require or die sprintf 
41         "Couldn't load the model class %s: %s", $config->model, $@;
42     
43     $config->model->setup_database($config, $calling_class, @_);
44     
45     foreach my $subclass ( @{ $config->classes } ) 
46     {
47         no strict 'refs';
48         unshift @{ $subclass . "::ISA" }, $config->model;
49         $config->model->adopt($subclass)
50           if $config->model->can("adopt");
51     }
52 }
53
54 sub init 
55 {
56     my $class  = shift;
57     my $config = $class->config;
58     $config->view || $config->view("Maypole::View::TT");
59     $config->view->require;
60     die "Couldn't load the view class " . $config->view . ": $@" if $@;
61     $config->display_tables
62       || $config->display_tables( $class->config->tables );
63     $class->view_object( $class->config->view->new );
64     $class->init_done(1);
65
66 }
67
68 sub new
69 {
70     my ($class) = @_;
71     
72     my $self = bless {
73         template_args => {},
74         config        => $class->config,
75     }, $class;
76     
77     return $self;
78 }
79
80 # handler() has a method attribute so that mod_perl will invoke
81 # BeerDB->handler() as a method rather than a plain function
82 # BeerDB::handler() and so this inherited implementation will be
83 # found. See e.g. "Practical mod_perl"  by Bekman & Cholet for
84 # more information <http://modperlbook.org/html/ch25_01.html>
85 sub handler : method 
86 {
87     # See Maypole::Workflow before trying to understand this.
88     my ($class, $req) = @_;
89     
90     $class->init unless $class->init_done;
91
92     my $self = $class->new;
93     
94     # initialise the request
95     $self->headers_out(Maypole::Headers->new);
96     $self->get_request($req);
97     $self->parse_location;
98     
99     my $status = $self->handler_guts;
100     
101     # moving this here causes unit test failures - need to check why
102     # before committing the move
103     #$status = $self->__call_process_view unless $self->output;
104     
105     return $status unless $status == OK;
106     
107     $self->send_output;
108     
109     return $status;
110 }
111
112 # The root of all evil
113 sub handler_guts 
114 {
115     my ($self) = @_;
116     
117     $self->__load_model;
118
119     my $applicable = $self->is_model_applicable;
120     
121     $self->__setup_plain_template unless $applicable;
122     
123     # We authenticate every request, needed for proper session management
124     my $status;
125     
126     eval { $status = $self->call_authenticate };
127     
128     if ( my $error = $@ ) 
129     {
130         $status = $self->call_exception($error);
131         
132         if ( $status != OK ) 
133         {
134             warn "caught authenticate error: $error";
135             return $self->debug ? 
136                     $self->view_object->error($self, $error) : ERROR;
137         }
138     }
139     
140     if ( $self->debug and $status != OK and $status != DECLINED ) 
141     {
142         $self->view_object->error( $self,
143             "Got unexpected status $status from calling authentication" );
144     }
145     
146     return $status unless $status == OK;
147
148     # We run additional_data for every request
149     $self->additional_data;
150     
151     if ($applicable) 
152     {
153         eval { $self->model_class->process($self) };
154         
155         if ( my $error = $@ ) 
156         {
157             $status = $self->call_exception($error);
158             
159             if ( $status != OK ) 
160             {
161                 warn "caught model error: $error";
162                 return $self->debug ? 
163                     $self->view_object->error($self, $error) : ERROR;
164             }
165         }
166     }
167     
168     # less frequent path - perhaps output has been set to an error message
169     return OK if $self->output;
170     
171     # normal path - no output has been generated yet
172     return $self->__call_process_view;
173 }
174
175 # is_applicable() returned false, so set up a plain template. Model processing 
176 # will be skipped, but need to remove the model anyway so the template can't 
177 # access it. 
178 sub __setup_plain_template
179 {
180     my ($self) = @_;
181     
182     # It's just a plain template
183     $self->model_class(undef);
184     
185     my $path = $self->path;
186     $path =~ s{/$}{};    # De-absolutify
187     $self->path($path);
188     
189     $self->template($self->path);
190 }
191
192 # The model has been processed or skipped (if is_applicable returned false), 
193 # any exceptions have been handled, and there's no content in $self->output
194 sub __call_process_view
195 {
196     my ($self) = @_;
197     
198     my $status;
199     
200     eval { $status = $self->view_object->process($self) };
201     
202     if ( my $error = $@ ) 
203     {
204         $status = $self->call_exception($error);
205         
206         if ( $status != OK ) 
207         {
208             warn "caught view error: $error" if $self->debug;
209             return $self->debug ? 
210                 $self->view_object->error($self, $error) : ERROR;
211         }
212     }
213     
214     return $status;
215 }
216
217 sub __load_model
218 {
219     my ($self) = @_;
220     $self->model_class( $self->config->model->class_of($self, $self->table) );
221 }
222
223 # is_applicable() should return true or false, not OK or DECLINED, because 
224 # the return value is never used as the return value from handler(). There's 
225 # probably a lot of code out there supplying the return codes though, so
226 # instead of changing is_applicable() to return 0 or 1, the return value is
227 # passed through __to_boolean. I think it helps handler_guts() if we don't
228 # have multiple sets of return codes being checked for different things -drb.
229 sub is_model_applicable 
230 {
231     my ($self) = @_;
232     
233     # cater for applications that are using obsolete version
234     if ($self->can('is_applicable')) 
235     {
236         warn "DEPRECATION WARNING: rewrite is_applicable to the interface ".
237                 "of Maypole::is_model_applicable\n";
238         return $self->is_applicable == OK;
239     }
240
241     # Establish which tables should be processed by the model
242     my $config = $self->config;
243     
244     $config->ok_tables || $config->ok_tables( $config->display_tables );
245     
246     $config->ok_tables( { map { $_ => 1 } @{ $config->ok_tables } } )
247         if ref $config->ok_tables eq "ARRAY";
248         
249     my $ok_tables = $config->ok_tables;
250       
251     # Does this request concern a table to be processed by the model?
252     my $table = $self->table;
253     
254     my $ok = 0;
255     
256     if (exists $ok_tables->{$table}) 
257     {
258         $ok = 1;
259     } 
260 # implements tj's default_table_view(), but there's no _default_table_view()
261 # or _have_default_table_view() yet
262 #    else 
263 #    {
264 #        $ok = $self->default_table_view($self->path, $self->args)
265 #            if $self->_have_default_table_view;
266 #    }
267
268     if (not $ok) 
269     {
270         warn "We don't have that table ($table).\n"
271             . "Available tables are: "
272             . join( ",", keys %$ok_tables )
273                 if $self->debug and not $ok_tables->{$table};
274                 
275         return 0;
276     }
277     
278     # Is the action public?
279     my $action = $self->action;
280     return 1 if $self->model_class->is_public($action);
281     
282     warn "The action '$action' is not applicable to the table $table"
283         if $self->debug;
284     
285     return 0;
286 }
287
288 sub call_authenticate 
289 {
290     my ($self) = @_;
291
292     # Check if we have a model class with an authenticate() to delegate to
293     return $self->model_class->authenticate($self) 
294         if $self->model_class and $self->model_class->can('authenticate');
295     
296     # Interface consistency is a Good Thing - 
297     # the invocant and the argument may one day be different things 
298     # (i.e. controller and request), like they are when authenticate() 
299     # is called on a model class (i.e. model and request)
300     return $self->authenticate($self);   
301 }
302
303 sub call_exception 
304 {
305     my ($self, $error) = @_;
306
307     # Check if we have a model class with an exception() to delegate to
308     if ( $self->model_class && $self->model_class->can('exception') )
309     {
310         my $status = $self->model_class->exception( $self, $error );
311         return $status if $status == OK;
312     }
313     
314     return $self->exception($error);
315 }
316
317 sub default_table_view {
318   my ($self,$path,$args) = @_;
319   my $path_is_ok = 0;
320   my $default_table_view = __PACKAGE__->_default_table_view;
321   # (path class action field)
322   my @path = $self->{path} =~ m{([^/]+)/?}g;
323   my $search_value = shift(@path);
324   if ($default_table_view->{path}) {
325     if ($default_table_view->{path} eq $search_value) {
326       $search_value = shift(@path);
327     } else {
328       return 0;
329     }
330   }
331
332   $self->{table} = $default_table_view->{class};
333   $self->{action} = $default_table_view->{action};
334   $self->{args} = [ $search_value,@path ];
335   return $path_is_ok;
336 }
337
338 sub additional_data { }
339
340 sub authenticate { return OK }
341
342 sub exception { return ERROR }
343
344 sub parse_path 
345 {
346     my ($self) = @_;
347     
348     $self->path || $self->path('frontpage');
349     
350     my @pi = grep {length} split '/', $self->path;
351     
352     $self->table(shift @pi);
353     
354     $self->action( shift @pi or 'index' );
355     
356     $self->args(\@pi);
357 }
358
359
360 sub make_path
361 {
362     my $r = shift;
363     
364     my %args;
365     
366     if (@_ == 1 and ref $_[0] and ref $_[0] eq 'HASH')
367     {
368         %args = %{$_[0]};
369     }
370     elsif ( @_ > 1 and @_ < 4 )
371     {
372         $args{table}      = shift;
373         $args{action}     = shift;
374         $args{additional} = shift;
375     }
376     else
377     {
378         %args = @_;
379     }
380     
381     do { die "no $_" unless $args{$_} } for qw( table action );    
382
383     my $additional = $args{additional} || $args{id};
384     
385     my @add = ();
386     
387     if ($additional)
388     {
389         # if $additional is a href, make_uri() will transform it into a query
390         @add = (ref $additional eq 'ARRAY') ? @$additional : ($additional);
391     }    
392     
393     my $uri = $r->make_uri($args{table}, $args{action}, @add);
394     
395     return $uri->as_string;
396 }
397
398 sub make_uri
399 {
400     my ($r, @segments) = @_;
401
402     my $query = (ref $segments[-1] eq 'HASH') ? pop(@segments) : undef;
403     
404     my $base = $r->config->uri_base; 
405     $base =~ s|/$||;
406     
407     my $uri = URI->new($base);
408     $uri->path_segments($uri->path_segments, grep {length} @segments);
409     
410     my $abs_uri = $uri->abs('/');
411     $abs_uri->query_form($query) if $query;
412     return $abs_uri;
413 }
414
415
416 # like CGI::param(), but read only 
417 sub param 
418
419     my ($self, $key) = @_;
420     
421     return keys %{$self->params} unless defined $key;
422     
423     return unless exists $self->params->{$key};
424     
425     my $val = $self->params->{$key};
426     
427     return ref $val ? @$val : ($val) if wantarray;
428         
429     return ref $val ? $val->[0] : $val;
430 }
431
432 sub get_template_root {'.'}
433 sub get_request       { }
434
435 sub get_protocol {
436   die "get_protocol is a virtual method. Do not use Maypole directly; use Apache::MVC or similar";
437 }
438
439 sub parse_location {
440     die "parse_location is a virtual method. Do not use Maypole directly; use Apache::MVC or similar";
441 }
442
443 sub redirect_request {
444   die "parse_location is a virtual method. Do not use Maypole directly; use Apache::MVC or similar";
445 }
446
447 sub redirect_internal_request {
448
449 }
450
451 sub send_output {
452     die "send_output is a virtual method. Do not use Maypole directly; use Apache::MVC or similar";
453 }
454
455 # Session and Repeat Submission Handling
456
457 sub make_random_id {
458     use Maypole::Session;
459     return Maypole::Session::generate_unique_id();
460 }
461
462 =head1 NAME
463
464 Maypole - MVC web application framework
465
466 =head1 SYNOPSIS
467
468 See L<Maypole::Application>.
469
470 =head1 DESCRIPTION
471
472 This documents the Maypole request object. See the L<Maypole::Manual>, for a
473 detailed guide to using Maypole.
474
475 Maypole is a Perl web application framework similar to Java's struts. It is 
476 essentially completely abstracted, and so doesn't know anything about
477 how to talk to the outside world.
478
479 To use it, you need to create a package which represents your entire
480 application. In our example above, this is the C<BeerDB> package.
481
482 This needs to first use L<Maypole::Application> which will make your package
483 inherit from the appropriate platform driver such as C<Apache::MVC> or
484 C<CGI::Maypole>, and then call setup.  This sets up the model classes and
485 configures your application. The default model class for Maypole uses
486 L<Class::DBI> to map a database to classes, but this can be changed by altering
487 configuration. (B<Before> calling setup.)
488
489 =head2 CLASS METHODS
490
491 =head3 config
492
493 Returns the L<Maypole::Config> object
494
495 =head3 setup
496
497     My::App->setup($data_source, $user, $password, \%attr);
498
499 Initialise the maypole application and model classes. Your application should
500 call this after setting configuration via L<"config">
501
502 =head3 init
503
504 You should not call this directly, but you may wish to override this to
505 add
506 application-specific initialisation.
507
508 =head3 new
509
510 Constructs a very minimal new Maypole request object.
511
512 =head3 view_object
513
514 Get/set the Maypole::View object
515
516 =head3 debug
517
518     sub My::App::debug {1}
519
520 Returns the debugging flag. Override this in your application class to
521 enable/disable debugging.
522
523 =head2 INSTANCE METHODS
524
525 =head3 parse_location
526
527 Turns the backend request (e.g. Apache::MVC, Maypole, CGI) into a
528 Maypole
529 request. It does this by setting the C<path>, and invoking C<parse_path>
530 and
531 C<parse_args>.
532
533 You should only need to define this method if you are writing a new
534 Maypole
535 backend.
536
537 =head3 path
538
539 Returns the request path
540
541 =head3 parse_path
542
543 Parses the request path and sets the C<args>, C<action> and C<table> 
544 properties
545
546 =head3 make_path( %args or \%args or @args )
547
548 This is the counterpart to C<parse_path>. It generates a path to use
549 in links, form actions etc. To implement your own path scheme, just override
550 this method and C<parse_path>.
551
552     %args = ( table      => $table,
553               action     => $action,        
554               additional => $additional,    # optional - generally an object ID
555               );
556               
557     \%args = as above, but a ref
558     
559     @args = ( $table, $action, $additional );   # $additional is optional
560
561 C<id> can be used as an alternative key to C<additional>.
562
563 C<$additional> can be a string, an arrayref, or a hashref. An arrayref is
564 expanded into extra path elements, whereas a hashref is translated into a query
565 string. 
566
567 =head3 make_uri( @segments )
568
569 Make a L<URI> object given table, action etc. Automatically adds
570 the C<uri_base>. 
571
572 If the final element in C<@segments> is a hash ref, C<make_uri> will render it
573 as a query string.
574
575 =head3 table
576
577 The table part of the Maypole request path
578
579 =head3 action
580
581 The action part of the Maypole request path
582
583 =head3 args
584
585 A list of remaining parts of the request path after table and action
586 have been
587 removed
588
589 =head3 headers_in
590
591 A L<Maypole::Headers> object containing HTTP headers for the request
592
593 =head3 headers_out
594
595 A L<HTTP::Headers> object that contains HTTP headers for the output
596
597 =head3 parse_args
598
599 Turns post data and query string paramaters into a hash of C<params>.
600
601 You should only need to define this method if you are writing a new
602 Maypole
603 backend.
604
605 =head3 param
606
607 An accessor for request parameters. It behaves similarly to CGI::param() for
608 accessing CGI parameters.
609
610 =head3 params
611
612 Returns a hash of request parameters. The source of the parameters may vary
613 depending on the Maypole backend, but they are usually populated from request
614 query string and POST data.
615
616 B<Note:> Where muliple values of a parameter were supplied, the
617 C<params> 
618 value
619 will be an array reference.
620
621 =head3 get_template_root
622
623 Implementation-specific path to template root.
624
625 You should only need to define this method if you are writing a new
626 Maypole
627 backend. Otherwise, see L<Maypole::Config/"template_root">
628
629 =head3 get_request
630
631 You should only need to define this method if you are writing a new
632 Maypole backend. It should return something that looks like an Apache
633 or CGI request object, it defaults to blank.
634
635 =head3 default_table_view
636
637 =head3 is_applicable
638
639 Returns a Maypole::Constant to indicate whether the request is valid.
640
641 B<This method is deprecated> as of version 2.11. If you have overridden it,
642 please override C<is_model_applicable> instead, and change the return type
643 from Maypole:Constants to true/false.
644
645 =head3 is_model_applicable
646
647 Returns true or false to indicate whether the request is valid.
648
649 The default implementation checks that C<< $r->table >> is publicly
650 accessible and that the model class is configured to handle the
651 C<< $r->action >>.
652
653 =head3 authenticate
654
655 Returns a Maypole::Constant to indicate whether the user is
656 authenticated for
657 the Maypole request.
658
659 The default implementation returns C<OK>
660
661 =head3 model_class
662
663 Returns the perl package name that will serve as the model for the
664 request. It corresponds to the request C<table> attribute.
665
666 =head3 additional_data
667
668 Called before the model processes the request, this method gives you a
669 chance
670 to do some processing for each request, for example, manipulating
671 C<template_args>.
672
673 =head3 objects
674
675 Get/set a list of model objects. The objects will be accessible in the
676 view
677 templates.
678
679 If the first item in C<$self-E<gt>args> can be C<retrieve()>d by the model
680 class,
681 it will be removed from C<args> and the retrieved object will be added
682 to the
683 C<objects> list. See L<Maypole::Model> for more information.
684
685 =head3 template_args
686
687     $self->template_args->{foo} = 'bar';
688
689 Get/set a hash of template variables.
690
691 =head3 stash
692
693 A place to put custom application data. Not used by Maypole itself. 
694
695 =head3 template
696
697 Get/set the template to be used by the view. By default, it returns
698 C<$self-E<gt>action>
699
700 =head3 exception
701
702 This method is called if any exceptions are raised during the
703 authentication 
704 or
705 model/view processing. It should accept the exception as a parameter and 
706 return
707 a Maypole::Constant to indicate whether the request should continue to
708 be
709 processed.
710
711 =head3 error
712
713 Get/set a request error
714
715 =head3 output
716
717 Get/set the response output. This is usually populated by the view
718 class. You
719 can skip view processing by setting the C<output>.
720
721 =head3 document_encoding
722
723 Get/set the output encoding. Default: utf-8.
724
725 =head3 content_type
726
727 Get/set the output content type. Default: text/html
728
729 =head3 send_output
730
731 Sends the output and additional headers to the user.
732
733 =head3 call_authenticate
734
735 This method first checks if the relevant model class
736 can authenticate the user, or falls back to the default
737 authenticate method of your Maypole application.
738
739
740 =head3 call_exception
741
742 This model is called to catch exceptions, first after authenticate, then after
743 processing the model class, and finally to check for exceptions from the view
744 class.
745
746 This method first checks if the relevant model class
747 can handle exceptions the user, or falls back to the default
748 exception method of your Maypole application.
749
750 =head3 make_random_id
751
752 returns a unique id for this request can be used to prevent or detect repeat
753 submissions.
754
755 =head3 get_protocol
756
757 Returns the protocol the request was made with, i.e. https
758
759 =head3 redirect_request
760
761 Sets output headers to redirect based on the arguments provided
762
763 Accepts either a single argument of the full url to redirect to, or a hash of named parameters :
764
765 $r->redirect_request('http://www.example.com/path');
766
767 or
768
769 $r->redirect_request(protocol=>'https', domain=>'www.example.com', path=>'/path/file?arguments', status=>'302', url=>'..');
770
771 The named parameters are protocol, domain, path, status and url
772
773 Only 1 named parameter is required but other than url, they can be combined as required and current values (from the request) will be used in place of any missing arguments. The url argument must be a full url including protocol and can only be combined with status.
774
775 =head3 redirect_internal_request 
776
777 =head3 handler
778
779 This method sets up the class if it's not done yet, sets some
780 defaults and leaves the dirty work to handler_guts.
781
782 =head3 handler_guts
783
784 This is the main request handling method and calls various methods to handle the request/response
785 and defines the workflow within Maypole.
786
787 Currently undocumented and liable to be refactored without warning.
788
789 =head1 SEE ALSO
790
791 There's more documentation, examples, and a information on our mailing lists
792 at the Maypole web site:
793
794 L<http://maypole.perl.org/>
795
796 L<Maypole::Application>, L<Apache::MVC>, L<CGI::Maypole>.
797
798 =head1 AUTHOR
799
800 Maypole is currently maintained by Simon Flack C<simonflk#cpan.org>
801
802 =head1 AUTHOR EMERITUS
803
804 Simon Cozens, C<simon#cpan.org>
805
806 Sebastian Riedel, C<sri#oook.de> maintained Maypole from 1.99_01 to 2.04
807
808 =head1 THANKS TO
809
810 Sebastian Riedel, Danijel Milicevic, Dave Slack, Jesse Sheidlower, Jody Belka,
811 Marcus Ramberg, Mickael Joanne, Randal Schwartz, Simon Flack, Steve Simms,
812 Veljko Vidovic and all the others who've helped.
813
814 =head1 LICENSE
815
816 You may distribute this code under the same terms as Perl itself.
817
818 =cut
819
820 1;