]> git.decadent.org.uk Git - maypole.git/blob - lib/Maypole/View/Base.pm
+ fix #7917 - use correct template if object creation fails
[maypole.git] / lib / Maypole / View / Base.pm
1 package Maypole::View::Base;
2 use File::Spec;
3 use UNIVERSAL::moniker;
4 use strict;
5 use Maypole::Constants;
6 use Carp;
7
8 sub new { bless {}, shift }    # By default, do nothing.
9
10 sub paths {
11     my ( $self, $r ) = @_;
12     my $root = $r->config->template_root || $r->get_template_root;
13     return (
14         $root,
15         (
16             $r->model_class
17               && File::Spec->catdir( $root, $r->model_class->moniker )
18         ),
19         File::Spec->catdir( $root, "custom" ),
20         File::Spec->catdir( $root, "factory" )
21     );
22 }
23
24 sub vars {
25     my ( $self, $r ) = @_;
26     my $class = $r->model_class;
27     my $base  = $r->config->uri_base;
28     $base =~ s/\/+$//;
29     my %args = (
30         request => $r,
31         objects => $r->objects,
32         base    => $base,
33         config  => $r->config
34
35           # ...
36     );
37     if ($class) {
38         my $classmeta = $args{classmetadata} ||= {};
39         $classmeta->{name}              ||= $class;
40         $classmeta->{description}       ||= $class->description;
41         $classmeta->{table}             ||= $class->table;
42         $classmeta->{columns}           ||= [ $class->display_columns ];
43         $classmeta->{list_columns}      ||= [ $class->list_columns ];
44         $classmeta->{colnames}          ||= { $class->column_names };
45         $classmeta->{related_accessors} ||= [ $class->related($r) ];
46         $classmeta->{moniker}           ||= $class->moniker;
47         $classmeta->{plural}            ||= $class->plural_moniker;
48         $classmeta->{cgi}               ||= { $class->to_cgi };
49
50         # User-friendliness facility for custom template writers.
51         if ( @{ $r->objects || [] } > 1 ) {
52             $args{ $r->model_class->plural_moniker } = $r->objects;
53         }
54         else {
55             ( $args{ $r->model_class->moniker } ) = @{ $r->objects || [] };
56         }
57     }
58
59     # Overrides
60     local $r->{template_args} = $r->{template_args};
61     delete $r->{template_args}{classmetadata}; # already overrides
62     %args = ( %args, %{ $r->{template_args} || {} } );
63     %args;
64 }
65
66 sub process {
67     my ( $self, $r ) = @_;
68     $r->{content_type}      ||= "text/html";
69     $r->{document_encoding} ||= "utf-8";
70     my $status = $self->template($r);
71     return $self->error($r) if $status != OK;
72     return OK;
73 }
74
75 sub error {
76     my ( $self, $r, $desc ) = @_;
77     $desc = $desc ? "$desc: " : "";
78     carp $desc . $r->{error};
79     if ( $r->{error} =~ /not found$/ ) {
80
81         # This is a rough test to see whether or not we're a template or
82         # a static page
83         return -1 unless @{ $r->{objects} || [] };
84
85         $r->{error} = <<EOF;
86
87 <H1> Template not found </H1>
88
89 This template was not found while processing the following request:
90
91 <B>@{[$r->{action}]}</B> on table <B>@{[ $r->{table} ]}</B> with objects:
92
93 <PRE>
94 @{[join "\n", @{$r->{objects}}]}
95 </PRE>
96
97 Looking for template <B>@{[$r->{template}]}</B> in paths:
98
99 <PRE>
100 @{[ join "\n", $self->paths($r) ]}
101 </PRE>
102 EOF
103         $r->{content_type} = "text/html";
104         $r->{output}       = $r->{error};
105         return OK;
106     }
107     $r->{content_type} = "text/plain";
108     $r->{output}       = $r->{error};
109     $r->send_output;
110     return ERROR;
111 }
112
113 sub template { die shift() . " didn't define a decent template method!" }
114
115 1;
116
117
118 =head1 NAME
119
120 Maypole::View::Base - Base cl
121
122 =head1 DESCRIPTION
123
124 This is the base class for Maypole view classes. This is an abstract class
125 meant to define the interface, and can't be used directly.
126
127 =head2 process
128
129 This is the engine of this module. It populates all the relevant variables
130 and calls the requested action.
131
132 Anyone subclassing this for a different database abstraction mechanism
133 needs to provide the following methods:
134
135 =head2 template 
136
137 In this method you do the actual processing of your template. it should use L<paths> 
138 to search for components, and provide the templates with easy access to the contents
139 of L<vars>. It should put the result in $r->{output} and return OK if processing was
140 sucessfull, or populate $r->{error} and return ERROR if it fails.
141
142 =head1 Other overrides
143
144 Additionally, individual derived model classes may want to override the
145
146 =head2 new
147
148 The default constructor does nothing. You can override this to perform actions
149 during view initialization.
150
151 =head2 paths
152
153 Returns search paths for templates. the default method returns factory, custom and
154 <tablename> under the configured template root.
155
156 =head2 vars
157
158 returns a hash of data the template should have access to. The default one populates
159 classmetadata if there is a class, as well as setting the data objects by name if 
160 there is one or more objects available.
161
162 =head2 error
163
164
165 =cut