Fix for RT#32215 ticket for multipart not coming through
[catagits/Catalyst-View-Email.git] / lib / Catalyst / View / Email / Template.pm
CommitLineData
529915ab 1package Catalyst::View::Email::Template;
2
3use warnings;
4use strict;
5
6use Class::C3;
7use Carp;
ea115f9b 8use Scalar::Util qw/ blessed /;
529915ab 9
10use Email::MIME::Creator;
11
ea115f9b 12use base qw/ Catalyst::View::Email /;
529915ab 13
ab4326b4 14our $VERSION = '0.11';
529915ab 15
16=head1 NAME
17
18Catalyst::View::Email::Template - Send Templated Email from Catalyst
19
20=head1 SYNOPSIS
21
4a44bcd3 22Sends templated mail, based upon your default view. It captures the output
529915ab 23of the rendering path, slurps in based on mime-types and assembles a multi-part
4a44bcd3 24email using L<Email::MIME::Creator> and sends it out.
529915ab 25
ea115f9b 26=head1 CONFIGURATION
27
4a44bcd3 28WARNING: since version 0.10 the configuration options slightly changed!
29
30Use the helper to create your view:
ea115f9b 31
32 $ script/myapp_create.pl view Email::Template Email::Template
33
4a44bcd3 34For basic configuration look at L<Catalyst::View::Email/CONFIGURATION>.
35
ea115f9b 36In your app configuration (example in L<YAML>):
529915ab 37
38 View::Email::Template:
529915ab 39 # Optional prefix to look somewhere under the existing configured
ea115f9b 40 # template paths.
41 # Default: none
529915ab 42 template_prefix: email
06afcdbc 43 # Define the defaults for the mail
44 default:
ea115f9b 45 # Defines the default view used to render the templates.
46 # If none is specified neither here nor in the stash
47 # Catalysts default view is used.
48 # Warning: if you don't tell Catalyst explicit which of your views should
49 # be its default one, C::V::Email::Template may choose the wrong one!
06afcdbc 50 view: TT
529915ab 51
52=head1 SENDING EMAIL
53
4a44bcd3 54Sending email works just like for L<Catalyst::View::Email> but by specifying
55the template instead of the body and forwarding to your Email::Template view:
56
57 sub controller : Private {
58 my ( $self, $c ) = @_;
59
60 $c->stash->{email} = {
61 to => 'jshirley@gmail.com',
62 cc => 'abraxxa@cpan.org',
63 bcc => [ qw/hidden@secret.com hidden2@foobar.com/ ],
64 from => 'no-reply@foobar.com',
65 subject => 'I am a Catalyst generated email',
66 template => 'test.tt',
ab4326b4 67 content_type => 'multipart/alternative'
4a44bcd3 68 };
69
70 $c->forward( $c->view('Email::Template') );
71 }
529915ab 72
06afcdbc 73Alternatively if you want more control over your templates you can use the following idiom
74to override the defaults:
12c85b56 75
76 templates => [
06afcdbc 77 {
78 template => 'email/test.html.tt',
79 content_type => 'text/html',
ea115f9b 80 charset => 'utf-8',
06afcdbc 81 view => 'TT',
82 },
83 {
84 template => 'email/test.plain.mason',
85 content_type => 'text/plain',
ea115f9b 86 charset => 'utf-8',
06afcdbc 87 view => 'Mason',
88 }
12c85b56 89 ]
90
91
4a44bcd3 92=head1 HANDLING ERRORS
93
94See L<Catalyst::View::Email/HANDLING ERRORS>.
529915ab 95
96=cut
97
06afcdbc 98# here the defaults of Catalyst::View::Email are extended by the additional
99# ones Template.pm needs.
100
529915ab 101__PACKAGE__->config(
102 template_prefix => '',
103);
104
105
106# This view hitches into your default view and will call the render function
107# on the templates provided. This means that you have a layer of abstraction
108# and you aren't required to modify your templates based on your desired engine
109# (Template Toolkit or Mason, for example). As long as the view adequately
110# supports ->render, all things are good. Mason, and others, are not good.
111
112#
113# The path here is to check configuration for the template root, and then
114# proceed to call render on the subsequent templates and stuff each one
115# into an Email::MIME container. The mime-type will be stupidly guessed with
116# the subdir on the template.
117#
529915ab 118
06afcdbc 119# Set it up so if you have multiple parts, they're alternatives.
120# This is on the top-level message, not the individual parts.
121#multipart/alternative
529915ab 122
06afcdbc 123sub _validate_view {
124 my ($self, $view) = @_;
125
4a44bcd3 126 croak "C::V::Email::Template's configured view '$view' isn't an object!"
06afcdbc 127 unless (blessed($view));
128
4a44bcd3 129 croak "C::V::Email::Template's configured view '$view' isn't an Catalyst::View!"
06afcdbc 130 unless ($view->isa('Catalyst::View'));
131
4a44bcd3 132 croak "C::V::Email::Template's configured view '$view' doesn't have a render method!"
06afcdbc 133 unless ($view->can('render'));
134}
529915ab 135
4a44bcd3 136=head1 METHODS
137
138=over 4
11a0bf18 139
4a44bcd3 140=item generate_part
141
142Generates a MIME part to include in the email. Since the email is template based
11a0bf18 143every template piece is a separate part that is included in the email.
144
145=cut
146
43090696 147sub generate_part {
06afcdbc 148 my ($self, $c, $attrs) = @_;
d0e11256 149
06afcdbc 150 my $template_prefix = $self->{template_prefix};
151 my $default_view = $self->{default}->{view};
152 my $default_content_type = $self->{default}->{content_type};
ea115f9b 153 my $default_charset = $self->{default}->{charset};
154
ea115f9b 155 my $view;
156 # use the view specified for the email part
157 if (exists $attrs->{view} && defined $attrs->{view} && $attrs->{view} ne '') {
158 $view = $c->view($attrs->{view});
159 $c->log->debug("C::V::Email::Template uses specified view $view for rendering.") if $c->debug;
160 }
161 # if none specified use the configured default view
162 elsif ($default_view) {
163 $view = $c->view($default_view);
164 $c->log->debug("C::V::Email::Template uses default view $view for rendering.") if $c->debug;;
165 }
166 # else fallback to Catalysts default view
167 else {
168 $view = $c->view;
c649c40e 169 $c->log->debug("C::V::Email::Template uses Catalysts default view $view for rendering.") if $c->debug;;
ea115f9b 170 }
06afcdbc 171
06afcdbc 172 # validate the per template view
173 $self->_validate_view($view);
06afcdbc 174
175 # prefix with template_prefix if configured
176 my $template = $template_prefix ne '' ? join('/', $template_prefix, $attrs->{template}) : $attrs->{template};
ea115f9b 177
43090696 178 # setup the attributes (merge with defaults)
d0e11256 179 my $e_m_attrs = $self->setup_attributes($c, $attrs);
ea115f9b 180
06afcdbc 181 # render the email part
182 my $output = $view->render( $c, $template, {
ea115f9b 183 content_type => $e_m_attrs->{content_type},
184 stash_key => $self->{stash_key},
43090696 185 %{$c->stash},
06afcdbc 186 });
43090696 187
188 if ( ref $output ) {
189 croak $output->can('as_string') ? $output->as_string : $output;
529915ab 190 }
43090696 191
06afcdbc 192 return Email::MIME->create(
ea115f9b 193 attributes => $e_m_attrs,
43090696 194 body => $output,
06afcdbc 195 );
196}
529915ab 197
4a44bcd3 198=item process
11a0bf18 199
4a44bcd3 200The process method is called when the view is dispatched to. This creates the
11a0bf18 201multipart message and then sends the message contents off to
4a44bcd3 202L<Catalyst::View::Email> for processing, which in turn hands off to
203L<Email::Send>.
11a0bf18 204
205=cut
206
06afcdbc 207sub process {
208 my ( $self, $c ) = @_;
529915ab 209
06afcdbc 210 # don't validate template_prefix
8b10ee55 211
ea115f9b 212 # the default view is validated if used
12c85b56 213
06afcdbc 214 # the content type should be validated by Email::MIME::Creator
ea115f9b 215
216 my $stash_key = $self->{stash_key};
12c85b56 217
06afcdbc 218 croak "No template specified for rendering"
219 unless $c->stash->{$stash_key}->{template}
220 or $c->stash->{$stash_key}->{templates};
221
222 # this array holds the Email::MIME objects
223 # in case of the simple api only one
224 my @parts = ();
12c85b56 225
06afcdbc 226 # now find out if the single or multipart api was used
227 # prefer the multipart one
228
229 # multipart api
230 if ($c->stash->{$stash_key}->{templates}
231 && ref $c->stash->{$stash_key}->{templates} eq 'ARRAY'
232 && ref $c->stash->{$stash_key}->{templates}[0] eq 'HASH') {
233 # loop through all parts of the mail
234 foreach my $part (@{$c->stash->{$stash_key}->{templates}}) {
43090696 235 push @parts, $self->generate_part($c, {
06afcdbc 236 view => $part->{view},
237 template => $part->{template},
238 content_type => $part->{content_type},
ea115f9b 239 charset => $part->{charset},
06afcdbc 240 });
43090696 241 }
06afcdbc 242 }
243 # single part api
244 elsif($c->stash->{$stash_key}->{template}) {
43090696 245 push @parts, $self->generate_part($c, {
06afcdbc 246 template => $c->stash->{$stash_key}->{template},
247 });
248 }
249
8b10ee55 250 delete $c->stash->{$stash_key}->{body};
251 $c->stash->{$stash_key}->{parts} ||= [];
252 push @{$c->stash->{$stash_key}->{parts}}, @parts;
529915ab 253
254 # Let C::V::Email do the actual sending. We just assemble the tasty bits.
255 return $self->next::method($c);
256}
257
4a44bcd3 258=back
259
529915ab 260=head1 TODO
261
262=head2 ATTACHMENTS
263
264There needs to be a method to support attachments. What I am thinking is
265something along these lines:
25650747 266
529915ab 267 attachments => [
268 # Set the body to a file handle object, specify content_type and
269 # the file name. (name is what it is sent at, not the file)
270 { body => $fh, name => "foo.pdf", content_type => "application/pdf" },
271 # Or, specify a filename that is added, and hey, encoding!
272 { filename => "foo.gif", name => "foo.gif", content_type => "application/pdf", encoding => "quoted-printable" },
273 # Or, just a path to a file, and do some guesswork for the content type
274 "/path/to/somefile.pdf",
275 ]
276
277=head1 SEE ALSO
278
279=head2 L<Catalyst::View::Email> - Send plain boring emails with Catalyst
280
281=head2 L<Catalyst::Manual> - The Catalyst Manual
282
283=head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
284
285=head1 AUTHORS
286
287J. Shirley <jshirley@gmail.com>
288
12c85b56 289Simon Elliott <cpan@browsing.co.uk>
290
4a44bcd3 291Alexander Hartmaier <abraxxa@cpan.org>
06afcdbc 292
529915ab 293=head1 LICENSE
294
295This library is free software, you can redistribute it and/or modify it under
296the same terms as Perl itself.
297
298=cut
299
3001;