refactor of the API and mainly Catalyst::View::Email::Template
[catagits/Catalyst-View-Email.git] / lib / Catalyst / View / Email / Template.pm
1 package Catalyst::View::Email::Template;
2
3 use warnings;
4 use strict;
5
6 use Class::C3;
7 use Carp;
8 use Scalar::Util qw( blessed );
9
10 use Email::MIME::Creator;
11
12 use base qw|Catalyst::View::Email|;
13
14 our $VERSION = '0.07';
15
16 =head1 NAME
17
18 Catalyst::View::Email::Template - Send Templated Email from Catalyst
19
20 =head1 SYNOPSIS
21
22 Sends Templated mail, based upon your Default View.  Will capture the output
23 of the rendering path, slurps in based on mime-types and assembles a multi-part
24 email and sends it out.
25 It uses Email::MIME to create the mail.
26
27 =head2 CONFIGURATION
28
29     View::Email::Template:
30         # Optional prefix to look somewhere under the existing configured
31         # template  paths. Default is none.
32         template_prefix: email
33         # Where to look in the stash for the email information.
34         # 'email' is the default, so you don't have to specify it.
35         stash_key: email
36         # Define the defaults for the mail
37         default:
38             # Defines the default content type (mime type) for every template.
39             content_type: text/html
40             # Defines the default charset for every MIME part with the content
41             # type 'text'.
42             # According to RFC2049 such a MIME part without a charset should
43             # be treated as US-ASCII by the mail client.
44             # If the charset is not set it won't be set for all MIME parts
45             # without an overridden one.
46             charset: utf-8
47             # Defines the default view which is used to render the templates.
48             view: TT
49         # Setup how to send the email
50         # all those options are passed directly to Email::Send
51         sender:
52             mailer: SMTP
53             mailer_args:
54                 Host:       smtp.example.com # defaults to localhost
55                 username:   username
56                 password:   password
57
58 =head1 SENDING EMAIL
59
60 Sending email is just setting up your defaults, the stash key and forwarding to the view.
61
62     $c->stash->{email} = {
63         to      => 'jshirley@gmail.com',
64         from    => 'no-reply@foobar.com',
65         subject => 'I am a Catalyst generated email',
66         template => 'test.tt',
67     };
68     $c->forward('View::Email::Template');
69
70 Alternatively if you want more control over your templates you can use the following idiom
71 to override the defaults:
72
73     templates => [
74         {
75             template        => 'email/test.html.tt',
76             content_type    => 'text/html',
77             view            => 'TT', 
78         },
79         {
80             template        => 'email/test.plain.mason',
81             content_type    => 'text/plain',
82             view            => 'Mason', 
83         }
84     ]
85
86
87 If it fails $c->error will have the error message.
88
89 =cut
90
91 # here the defaults of Catalyst::View::Email are extended by the additional
92 # ones Template.pm needs.
93
94 __PACKAGE__->config(
95     template_prefix => '',
96 );
97
98
99 # This view hitches into your default view and will call the render function
100 # on the templates provided.  This means that you have a layer of abstraction
101 # and you aren't required to modify your templates based on your desired engine
102 # (Template Toolkit or Mason, for example).  As long as the view adequately
103 # supports ->render, all things are good.  Mason, and others, are not good.
104
105 #
106 # The path here is to check configuration for the template root, and then
107 # proceed to call render on the subsequent templates and stuff each one
108 # into an Email::MIME container.  The mime-type will be stupidly guessed with
109 # the subdir on the template.
110 #
111
112 # Set it up so if you have multiple parts, they're alternatives.
113 # This is on the top-level message, not the individual parts.
114 #multipart/alternative
115
116 sub _validate_view {
117     my ($self, $view) = @_;
118     
119     croak "Email::Template's configured view '$view' isn't an object!"
120         unless (blessed($view));
121
122     croak "Email::Template's configured view '$view' isn't an Catalyst::View!"
123         unless ($view->isa('Catalyst::View'));
124
125     croak "Email::Template's configured view '$view' doesn't have a render method!"
126         unless ($view->can('render'));
127 }
128
129 sub _generate_part {
130     my ($self, $c, $attrs) = @_;
131     
132     my $template_prefix         = $self->{template_prefix};
133     my $default_view            = $self->{default}->{view};
134     my $default_content_type    = $self->{default}->{content_type};
135
136     my $view = $c->view($attrs->{view} || $default_view);
137     # validate the per template view
138     $self->_validate_view($view);
139 #    $c->log->debug("VIEW: $view");
140     
141     # prefix with template_prefix if configured
142     my $template = $template_prefix ne '' ? join('/', $template_prefix, $attrs->{template}) : $attrs->{template};
143             
144     my $content_type = $attrs->{content_type} || $default_content_type;
145     
146     # render the email part
147     my $output = $view->render( $c, $template, { 
148         content_type    => "$content_type",
149         stash_key       => $self->stash_key,
150         %{$c->stash},
151     });
152                                 
153     if (ref $output) {
154         croak $output->can('as_string') ? $output->as_string : $output;
155     }
156         
157     return Email::MIME->create(
158         attributes => {
159             content_type => "$content_type",
160         },
161         body => $output,
162     );
163 }
164
165 sub process {
166     my ( $self, $c ) = @_;
167
168     # validate here so _generate_part doesn't have to do it for every part
169     my $stash_key = $self->{stash_key};
170     croak "Email::Template's stash_key isn't defined!"
171         if ($stash_key eq '');
172     
173     # don't validate template_prefix
174
175     # the default view is validated on use later anyways...
176     # but just to be sure even if not used
177 #    $self->_validate_view($c->view($self->{default}->{view}));
178
179 #    $c->log->debug("SELF: $self");
180 #    $c->log->debug('DEFAULT VIEW: ' . $self->{default}->{view});
181
182     # the content type should be validated by Email::MIME::Creator
183
184     croak "No template specified for rendering"
185         unless $c->stash->{$stash_key}->{template}
186             or $c->stash->{$stash_key}->{templates};
187     
188     # this array holds the Email::MIME objects
189     # in case of the simple api only one
190     my @parts = (); 
191
192     # now find out if the single or multipart api was used
193     # prefer the multipart one
194     
195     # multipart api
196     if ($c->stash->{$stash_key}->{templates}
197         && ref $c->stash->{$stash_key}->{templates} eq 'ARRAY'
198         && ref $c->stash->{$stash_key}->{templates}[0] eq 'HASH') {
199         # loop through all parts of the mail
200         foreach my $part (@{$c->stash->{$stash_key}->{templates}}) {
201             push @parts, $self->_generate_part($c, {
202                 view            => $part->{view},
203                 template        => $part->{template},
204                 content_type    => $part->{content_type},
205             });
206         }
207     }
208     # single part api
209     elsif($c->stash->{$stash_key}->{template}) {
210         push @parts, $self->_generate_part($c, {
211             template    => $c->stash->{$stash_key}->{template},
212         });
213     }
214     
215     delete $c->stash->{$stash_key}->{body};
216     $c->stash->{$stash_key}->{parts} ||= [];
217     push @{$c->stash->{$stash_key}->{parts}}, @parts;
218
219     # Let C::V::Email do the actual sending.  We just assemble the tasty bits.
220     return $self->next::method($c);
221 }
222
223 =head1 TODO
224
225 =head2 ATTACHMENTS
226
227 There needs to be a method to support attachments.  What I am thinking is
228 something along these lines:
229
230     attachments => [
231         # Set the body to a file handle object, specify content_type and
232         # the file name. (name is what it is sent at, not the file)
233         { body => $fh, name => "foo.pdf", content_type => "application/pdf" },
234         # Or, specify a filename that is added, and hey, encoding!
235         { filename => "foo.gif", name => "foo.gif", content_type => "application/pdf", encoding => "quoted-printable" },
236         # Or, just a path to a file, and do some guesswork for the content type
237         "/path/to/somefile.pdf",
238     ]
239
240 =head1 SEE ALSO
241
242 =head2 L<Catalyst::View::Email> - Send plain boring emails with Catalyst
243
244 =head2 L<Catalyst::Manual> - The Catalyst Manual
245
246 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
247
248 =head1 AUTHORS
249
250 J. Shirley <jshirley@gmail.com>
251
252 Simon Elliott <cpan@browsing.co.uk>
253
254 Alexander Hartmaier <alex_hartmaier@hotmail.com>
255
256 =head1 LICENSE
257
258 This library is free software, you can redistribute it and/or modify it under
259 the same terms as Perl itself.
260
261 =cut
262
263 1;