Fix for RT#32215 ticket for multipart not coming through
[catagits/Catalyst-View-Email.git] / lib / Catalyst / View / Email.pm
1 package Catalyst::View::Email;
2
3 use warnings;
4 use strict;
5
6 use Class::C3;
7 use Carp;
8
9 use Email::Send;
10 use Email::MIME::Creator;
11
12 use base qw/ Catalyst::View /;
13
14 our $VERSION = '0.11';
15
16 __PACKAGE__->mk_accessors(qw/ mailer /);
17
18 =head1 NAME
19
20 Catalyst::View::Email - Send Email from Catalyst
21
22 =head1 SYNOPSIS
23
24 This module sends out emails from a stash key specified in the
25 configuration settings.
26
27 =head1 CONFIGURATION
28
29 WARNING: since version 0.10 the configuration options slightly changed!
30
31 Use the helper to create your View:
32     
33     $ script/myapp_create.pl view Email Email
34
35 In your app configuration (example in L<YAML>):
36
37     View::Email:
38         # Where to look in the stash for the email information.
39         # 'email' is the default, so you don't have to specify it.
40         stash_key: email
41         # Define the defaults for the mail
42         default:
43             # Defines the default content type (mime type).
44             # mandatory
45             content_type: text/plain
46             # Defines the default charset for every MIME part with the content
47             # type text.
48             # According to RFC2049 a MIME part without a charset should
49             # be treated as US-ASCII by the mail client.
50             # If the charset is not set it won't be set for all MIME parts
51             # without an overridden one.
52             # Default: none
53             charset: utf-8
54         # Setup how to send the email
55         # all those options are passed directly to Email::Send
56         sender:
57             mailer: SMTP
58             # mailer_args is passed directly into Email::Send 
59             mailer_args:
60                 Host:       smtp.example.com # defaults to localhost
61                 username:   username
62                 password:   password
63
64 =head1 NOTE ON SMTP
65
66 If you use SMTP and don't specify Host, it will default to localhost and
67 attempt delivery. This often means an email will sit in a queue and
68 not be delivered.
69
70 =cut
71
72 __PACKAGE__->config(
73     stash_key   => 'email',
74     default     => {
75         content_type    => 'text/plain',
76     },
77 );
78
79 =head1 SENDING EMAIL
80
81 Sending email is just filling the stash and forwarding to the view:
82
83     sub controller : Private {
84         my ( $self, $c ) = @_;
85
86         $c->stash->{email} = {
87             to      => 'jshirley@gmail.com',
88             cc      => 'abraxxa@cpan.org',
89             bcc     => [ qw/hidden@secret.com hidden2@foobar.com/ ],
90             from    => 'no-reply@foobar.com',
91             subject => 'I am a Catalyst generated email',
92             body    => 'Body Body Body',
93         };
94         
95         $c->forward( $c->view('Email') );
96     }
97
98 Alternatively you can use a more raw interface and specify the headers as
99 an array reference like it is passed to L<Email::MIME::Creator>.
100 Note that you may also mix both syntaxes if you like ours better but need to
101 specify additional header attributes.
102 The attributes are appended to the header array reference without overwriting
103 contained ones.
104
105     $c->stash->{email} = {
106         header => [
107             To      => 'jshirley@gmail.com',
108             Cc      => 'abraxxa@cpan.org',
109             Bcc     => [ qw/hidden@secret.com hidden2@foobar.com/ ],
110             From    => 'no-reply@foobar.com',
111             Subject => 'Note the capitalization differences',
112         ],
113         body => qq{Ain't got no body, and nobody cares.},
114         # Or, send parts
115         parts => [
116             Email::MIME->create(
117                 attributes => {
118                     content_type => 'text/plain',
119                     disposition  => 'attachment',
120                     charset      => 'US-ASCII',
121                 },
122                 body => qq{Got a body, but didn't get ahead.},
123             )
124         ],
125     };
126
127 =head1 HANDLING ERRORS
128
129 If the email fails to send, the view will die (throw an exception).
130 After your forward to the view, it is a good idea to check for errors:
131     
132     $c->forward( $c->view('Email') );
133     
134     if ( scalar( @{ $c->error } ) ) {
135         $c->error(0); # Reset the error condition if you need to
136         $c->response->body('Oh noes!');
137     } else {
138         $c->response->body('Email sent A-OK! (At least as far as we can tell)');
139     }
140
141 =head1 USING TEMPLATES FOR EMAIL
142
143 Now, it's no fun to just send out email using plain strings.
144 Take a look at L<Catalyst::View::Email::Template> to see how you can use your
145 favourite template engine to render the mail body.
146
147 =head1 METHODS
148
149 =over 4
150
151 =item new
152
153 Validates the base config and creates the L<Email::Send> object for later use
154 by process.
155
156 =cut
157
158 sub new {
159     my $self = shift->next::method(@_);
160
161     my $stash_key = $self->{stash_key};
162     croak "$self stash_key isn't defined!"
163         if ($stash_key eq '');
164
165     my $sender = Email::Send->new;
166
167     if ( my $mailer = $self->{sender}->{mailer} ) {
168         croak "$mailer is not supported, see Email::Send"
169             unless $sender->mailer_available($mailer);
170         $sender->mailer($mailer);
171     }
172     else {
173         # Default case, run through the most likely options first.
174         for ( qw/SMTP Sendmail Qmail/ ) {
175             $sender->mailer($_) and last if $sender->mailer_available($_);
176         }
177     }
178
179     if ( my $args = $self->{sender}->{mailer_args} ) {
180         if ( ref $args eq 'HASH' ) {
181             $sender->mailer_args([ %$args ]);
182         }
183         elsif ( ref $args eq 'ARRAY' ) {
184             $sender->mailer_args($args);
185         } else {
186             croak "Invalid mailer_args specified, check pod for Email::Send!";
187         }
188     }
189
190     $self->mailer($sender);
191
192     return $self;
193 }
194
195 =item process($c)
196
197 The process method does the actual processing when the view is dispatched to.
198
199 This method sets up the email parts and hands off to L<Email::Send> to handle
200 the actual email delivery.
201
202 =cut
203
204 sub process {
205     my ( $self, $c ) = @_;
206
207     croak "Unable to send mail, bad mail configuration"
208         unless $self->mailer;
209
210     my $email  = $c->stash->{$self->{stash_key}};
211     croak "Can't send email without a valid email structure"
212         unless $email;
213
214     # Default content type
215     if ( exists $self->{content_type} and not $email->{content_type} ) {
216         $email->{content_type} = $self->{content_type};
217     }
218
219     my $header  = $email->{header} || [];
220         push @$header, ('To' => delete $email->{to})
221             if $email->{to};
222         push @$header, ('Cc' => delete $email->{cc})
223             if $email->{cc};
224         push @$header, ('Bcc' => delete $email->{bcc})
225             if $email->{bcc};
226         push @$header, ('From' => delete $email->{from})
227             if $email->{from};
228         push @$header, ('Subject' => delete $email->{subject})
229             if $email->{subject};
230         push @$header, ('Content-type' => $email->{content_type})
231             if $email->{content_type};
232
233     my $parts = $email->{parts};
234     my $body  = $email->{body};
235    
236     unless ( $parts or $body ) {
237         croak "Can't send email without parts or body, check stash";
238     }
239
240     my %mime = ( header => $header, attributes => {} );
241
242     if ( $parts and ref $parts eq 'ARRAY' ) {
243         $mime{parts} = $parts;
244     } else {
245         $mime{body} = $body;
246     }
247
248     $mime{attributes}->{content_type} = $email->{content_type} 
249         if $email->{content_type};
250     if ( $mime{attributes} and not $mime{attributes}->{charset} and
251          $self->{default}->{charset} )
252     {
253         $mime{attributes}->{charset} = $self->{default}->{charset};
254     }
255
256     my $message = $self->generate_message( $c, \%mime );
257
258     if ( $message ) {
259         my $return = $self->mailer->send($message);
260         # return is a Return::Value object, so this will stringify as the error
261         # in the case of a failure.  
262         croak "$return" if !$return;
263     } else {
264         croak "Unable to create message";
265     }
266 }
267
268 =item setup_attributes($c, $attr)
269
270 Merge attributes with the configured defaults. You can override this method to
271 return a structure to pass into L<generate_message> which subsequently
272 passes the return value of this method to Email::MIME->create under the
273 C<attributes> key.
274
275 =cut
276
277 sub setup_attributes {
278     my ( $self, $c, $attrs ) = @_;
279     
280     my $default_content_type    = $self->{default}->{content_type};
281     my $default_charset         = $self->{default}->{charset};
282
283     my $e_m_attrs = {};
284
285     if (exists $attrs->{content_type} && defined $attrs->{content_type} && $attrs->{content_type} ne '') {
286         $c->log->debug('C::V::Email uses specified content_type ' . $attrs->{content_type} . '.') if $c->debug;
287         $e_m_attrs->{content_type} = $attrs->{content_type};
288     }
289     elsif (defined $default_content_type && $default_content_type ne '') {
290         $c->log->debug("C::V::Email uses default content_type $default_content_type.") if $c->debug;
291         $e_m_attrs->{content_type} = $default_content_type;
292     }
293    
294     if (exists $attrs->{charset} && defined $attrs->{charset} && $attrs->{charset} ne '') {
295         $e_m_attrs->{charset} = $attrs->{charset};
296     }
297     elsif (defined $default_charset && $default_charset ne '') {
298         $e_m_attrs->{charset} = $default_charset;
299     }
300
301     return $e_m_attrs;
302 }
303
304 =item generate_message($c, $attr)
305
306 Generate a message part, which should be an L<Email::MIME> object and return it.
307
308 Takes the attributes, merges with the defaults as necessary and returns a
309 message object.
310
311 =cut
312
313 sub generate_message {
314     my ( $self, $c, $attr ) = @_;
315
316     # setup the attributes (merge with defaults)
317     $attr->{attributes} = $self->setup_attributes($c, $attr->{attributes});
318     return Email::MIME->create(%$attr);
319 }
320
321 =back
322
323 =head1 SEE ALSO
324
325 =head2 L<Catalyst::View::Email::Template> - Send fancy template emails with Cat
326
327 =head2 L<Catalyst::Manual> - The Catalyst Manual
328
329 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
330
331 =head1 AUTHORS
332
333 J. Shirley <jshirley@gmail.com>
334
335 Alexander Hartmaier <abraxxa@cpan.org>
336
337 =head1 CONTRIBUTORS
338
339 (Thanks!)
340
341 Matt S Trout
342
343 Daniel Westermann-Clark
344
345 Simon Elliott <cpan@browsing.co.uk>
346
347 Roman Filippov
348
349 =head1 LICENSE
350
351 This library is free software, you can redistribute it and/or modify it under
352 the same terms as Perl itself.
353
354 =cut
355
356 1;