8c6055d6e565c19c851b869a2dc3c46315ee730d
[catagits/Catalyst-View-Email.git] / lib / Catalyst / View / Email.pm
1 package Catalyst::View::Email;
2
3 use Moose;
4 use Carp;
5
6 use Encode qw(encode decode);
7 use Email::Sender::Simple qw/ sendmail /;
8 use Email::MIME::Creator;
9 extends 'Catalyst::View';
10
11 our $VERSION = '0.32';
12 $VERSION = eval $VERSION;
13
14 has 'mailer' => (
15     is      => 'rw',
16     isa     => 'Str',
17     lazy    => 1,
18     default => sub { "sendmail" }
19 );
20
21 has '_mailer_obj' => (
22     is      => 'rw',
23     does    => 'Email::Sender::Transport',
24     lazy    => 1,
25     builder => '_build_mailer_obj',
26 );
27
28 has 'stash_key' => (
29     is      => 'rw',
30     isa     => 'Str',
31     lazy    => 1,
32     default => sub { "email" }
33 );
34
35 has 'default' => (
36     is      => 'rw',
37     isa     => 'HashRef',
38     default => sub { { content_type => 'text/plain' } },
39     lazy    => 1,
40 );
41
42 has 'sender' => (
43     is      => 'rw',
44     isa     => 'HashRef',
45     lazy    => 1,
46     default => sub { { mailer => shift->mailer } }
47 );
48
49 has 'content_type' => (
50     is      => 'rw',
51     isa     => 'Str',
52     default => sub { shift->default->{content_type} },
53     lazy    => 1,
54 );
55
56 =head1 NAME
57
58 Catalyst::View::Email - Send Email from Catalyst
59
60 =head1 SYNOPSIS
61
62 This module sends out emails from a stash key specified in the
63 configuration settings.
64
65 =head1 CONFIGURATION
66
67 WARNING: since version 0.10 the configuration options slightly changed!
68
69 Use the helper to create your View:
70     
71     $ script/myapp_create.pl view Email Email
72
73 In your app configuration:
74
75     __PACKAGE__->config(
76         'View::Email' => {
77             # Where to look in the stash for the email information.
78             # 'email' is the default, so you don't have to specify it.
79             stash_key => 'email',
80             # Define the defaults for the mail
81             default => {
82                 # Defines the default content type (mime type). Mandatory
83                 content_type => 'text/plain',
84                 # Defines the default charset for every MIME part with the 
85                 # content type text.
86                 # According to RFC2049 a MIME part without a charset should
87                 # be treated as US-ASCII by the mail client.
88                 # If the charset is not set it won't be set for all MIME parts
89                 # without an overridden one.
90                 # Default: none
91                 charset => 'utf-8'
92             },
93             # Setup how to send the email
94             # all those options are passed directly to Email::Sender::Simple
95             sender => {
96                 # if mailer doesn't start with Email::Sender::Simple::Transport::,
97                 # then this is prepended.
98                 mailer => 'SMTP',
99                 # mailer_args is passed directly into Email::Sender::Simple 
100                 mailer_args => {
101                     host     => 'smtp.example.com', # defaults to localhost
102                     sasl_username => 'sasl_username',
103                     sasl_password => 'sasl_password',
104             }
105           }
106         }
107     );
108
109 =head1 NOTE ON SMTP
110
111 If you use SMTP and don't specify host, it will default to localhost and
112 attempt delivery. This often means an email will sit in a queue and
113 not be delivered.
114
115 =cut
116
117 =head1 SENDING EMAIL
118
119 Sending email is just filling the stash and forwarding to the view:
120
121     sub controller : Private {
122         my ( $self, $c ) = @_;
123
124         $c->stash->{email} = {
125             to      => 'jshirley@gmail.com',
126             cc      => 'abraxxa@cpan.org',
127             from    => 'no-reply@foobar.com',
128             subject => 'I am a Catalyst generated email',
129             body    => 'Body Body Body',
130         };
131         
132         $c->forward( $c->view('Email') );
133     }
134
135 Alternatively you can use a more raw interface and specify the headers as
136 an array reference like it is passed to L<Email::MIME::Creator>.
137 Note that you may also mix both syntaxes if you like ours better but need to
138 specify additional header attributes.
139 The attributes are appended to the header array reference without overwriting
140 contained ones.
141
142     $c->stash->{email} = {
143         header => [
144             To      => 'jshirley@gmail.com',
145             Cc      => 'abraxxa@cpan.org',
146             Bcc     => join ',', qw/hidden@secret.com hidden2@foobar.com/,
147             From    => 'no-reply@foobar.com',
148             Subject => 'Note the capitalization differences',
149         ],
150         body => qq{Ain't got no body, and nobody cares.},
151         # Or, send parts
152         parts => [
153             Email::MIME->create(
154                 attributes => {
155                     content_type => 'text/plain',
156                     disposition  => 'attachment',
157                     charset      => 'US-ASCII',
158                 },
159                 body => qq{Got a body, but didn't get ahead.},
160             )
161         ],
162     };
163
164 You can set the envelope sender and recipient as well:
165
166   $c->stash->{email} = {
167
168     envelope_from => 'envelope-from@example.com',
169     from          => 'header-from@example.com',
170
171     envelope_to   => [ 'foo@example.com', 'bar@example.com' ],
172     to            => 'Undisclosed Recipients:;',
173
174     ...
175   };
176
177 =head1 HANDLING ERRORS
178
179 If the email fails to send, the view will die (throw an exception).
180 After your forward to the view, it is a good idea to check for errors:
181     
182     $c->forward( $c->view('Email') );
183     
184     if ( scalar( @{ $c->error } ) ) {
185         $c->error(0); # Reset the error condition if you need to
186         $c->response->body('Oh noes!');
187     } else {
188         $c->response->body('Email sent A-OK! (At least as far as we can tell)');
189     }
190
191 =head1 USING TEMPLATES FOR EMAIL
192
193 Now, it's no fun to just send out email using plain strings.
194 Take a look at L<Catalyst::View::Email::Template> to see how you can use your
195 favourite template engine to render the mail body.
196
197 =head1 METHODS
198
199 =over 4
200
201 =item new
202
203 Validates the base config and creates the L<Email::Sender::Simple> object for later use
204 by process.
205
206 =cut
207
208 sub BUILD {
209     my $self = shift;
210
211     my $stash_key = $self->stash_key;
212     croak "$self stash_key isn't defined!"
213       if ( $stash_key eq '' );
214
215 }
216
217 sub _build_mailer_obj {
218     my ($self) = @_;
219     my $transport_class = ucfirst $self->sender->{mailer};
220
221     # borrowed from Email::Sender::Simple -- apeiron, 2010-01-26
222     if ( $transport_class !~ /^Email::Sender::Transport::/ ) {
223         $transport_class = "Email::Sender::Transport::$transport_class";
224     }
225
226     Class::MOP::load_class($transport_class);
227
228     return $transport_class->new( $self->sender->{mailer_args} || {} );
229 }
230
231 =item process($c)
232
233 The process method does the actual processing when the view is dispatched to.
234
235 This method sets up the email parts and hands off to L<Email::Sender::Simple> to handle
236 the actual email delivery.
237
238 =cut
239
240 sub process {
241     my ( $self, $c ) = @_;
242
243     croak "Unable to send mail, bad mail configuration"
244       unless $self->sender->{mailer};
245
246     my $email = $c->stash->{ $self->stash_key };
247     croak "Can't send email without a valid email structure"
248       unless $email;
249
250     # Default content type
251     if ( $self->content_type and not $email->{content_type} ) {
252         $email->{content_type} = $self->content_type;
253     }
254
255     my $header = $email->{header} || [];
256     push @$header, ( 'To' => delete $email->{to} )
257       if $email->{to};
258     push @$header, ( 'Cc' => delete $email->{cc} )
259       if $email->{cc};
260     push @$header, ( 'From' => delete $email->{from} )
261       if $email->{from};
262     push @$header,
263       ( 'Subject' => Encode::encode( 'MIME-Header', delete $email->{subject} ) )
264       if $email->{subject};
265     push @$header, ( 'Content-type' => $email->{content_type} )
266       if $email->{content_type};
267
268     my $parts = $email->{parts};
269     my $body  = $email->{body};
270
271     unless ( $parts or $body ) {
272         croak "Can't send email without parts or body, check stash";
273     }
274
275     my %mime = ( header => $header, attributes => {} );
276
277     if ( $parts and ref $parts eq 'ARRAY' ) {
278         $mime{parts} = $parts;
279     }
280     else {
281         $mime{body} = $body;
282     }
283
284     $mime{attributes}->{content_type} = $email->{content_type}
285       if $email->{content_type};
286     if (    $mime{attributes}
287         and not $mime{attributes}->{charset}
288         and $self->{default}->{charset} )
289     {
290         $mime{attributes}->{charset} = $self->{default}->{charset};
291     }
292
293     $mime{attributes}->{encoding} = $email->{encoding} 
294         if $email->{encoding};
295
296     my $message = $self->generate_message( $c, \%mime );
297
298     if ($message) {
299         my $return = sendmail( $message,
300           {
301             exists $email->{envelope_from} ? ( from => $email->{envelope_from} ) : (),
302             exists $email->{envelope_to}   ? ( to   => $email->{envelope_to}   ) : (),
303             transport => $self->_mailer_obj,
304           } );
305
306         # return is a Return::Value object, so this will stringify as the error
307         # in the case of a failure.
308         croak "$return" if !$return;
309     }
310     else {
311         croak "Unable to create message";
312     }
313 }
314
315 =item setup_attributes($c, $attr)
316
317 Merge attributes with the configured defaults. You can override this method to
318 return a structure to pass into L<generate_message> which subsequently
319 passes the return value of this method to Email::MIME->create under the
320 C<attributes> key.
321
322 =cut
323
324 sub setup_attributes {
325     my ( $self, $c, $attrs ) = @_;
326
327     my $default_content_type = $self->default->{content_type};
328     my $default_charset      = $self->default->{charset}; 
329     my $default_encoding     = $self->default->{encoding};
330
331     my $e_m_attrs = {};
332
333     if (   exists $attrs->{content_type}
334         && defined $attrs->{content_type}
335         && $attrs->{content_type} ne '' )
336     {
337         $c->log->debug( 'C::V::Email uses specified content_type '
338               . $attrs->{content_type}
339               . '.' )
340           if $c->debug;
341         $e_m_attrs->{content_type} = $attrs->{content_type};
342     }
343     elsif ( defined $default_content_type && $default_content_type ne '' ) {
344         $c->log->debug(
345             "C::V::Email uses default content_type $default_content_type.")
346           if $c->debug;
347         $e_m_attrs->{content_type} = $default_content_type;
348     }
349
350     if (   exists $attrs->{charset}
351         && defined $attrs->{charset}
352         && $attrs->{charset} ne '' )
353     {
354         $e_m_attrs->{charset} = $attrs->{charset};
355     }
356     elsif ( defined $default_charset && $default_charset ne '' ) {
357         $e_m_attrs->{charset} = $default_charset;
358     }
359
360     if ( exists $attrs->{encoding}
361          && defined $attrs->{encoding}
362          && $attrs->{encoding} ne '' )
363     {
364         $c->log->debug(
365         'C::V::Email uses specified encoding '
366         . $attrs->{encoding}
367         . '.' )
368          if $c->debug;
369          $e_m_attrs->{encoding} = $attrs->{encoding};
370     }
371      elsif ( defined $default_encoding && $default_encoding ne '' ) {
372          $c->log->debug(
373          "C::V::Email uses default encoding $default_encoding.")
374          if $c->debug;
375          $e_m_attrs->{encoding} = $default_encoding;
376      }
377
378     return $e_m_attrs;
379 }
380
381 =item generate_message($c, $attr)
382
383 Generate a message part, which should be an L<Email::MIME> object and return it.
384
385 Takes the attributes, merges with the defaults as necessary and returns a
386 message object.
387
388 =cut
389
390 sub generate_message {
391     my ( $self, $c, $attr ) = @_;
392
393     # setup the attributes (merge with defaultis)
394     $attr->{attributes} = $self->setup_attributes( $c, $attr->{attributes} );
395     Email::MIME->create( %$attr );
396 }
397
398 =back
399
400
401 =head1 TROUBLESHOOTING
402
403 As with most things computer related, things break.  Email even more so.  
404 Typically any errors are going to come from using SMTP as your sending method,
405 which means that if you are having trouble the first place to look is at
406 L<Email::Sender::Transport::SMTP>.  This module is just a wrapper for L<Email::Sender::Simple>,
407 so if you get an error on sending, it is likely from there anyway.
408
409 If you are using SMTP and have troubles sending, whether it is authentication
410 or a very bland "Can't send" message, make sure that you have L<Net::SMTP> and,
411 if applicable, L<Net::SMTP::SSL> installed.
412
413 It is very simple to check that you can connect via L<Net::SMTP>, and if you
414 do have sending errors the first thing to do is to write a simple script
415 that attempts to connect.  If it works, it is probably something in your
416 configuration so double check there.  If it doesn't, well, keep modifying
417 the script and/or your mail server configuration until it does!
418
419 =head1 SEE ALSO
420
421 =head2 L<Catalyst::View::Email::Template> - Send fancy template emails with Cat
422
423 =head2 L<Catalyst::Manual> - The Catalyst Manual
424
425 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
426
427 =head1 AUTHORS
428
429 J. Shirley <jshirley@gmail.com>
430
431 Alexander Hartmaier <abraxxa@cpan.org>
432
433 =head1 CONTRIBUTORS
434
435 (Thanks!)
436
437 Matt S Trout
438
439 Daniel Westermann-Clark
440
441 Simon Elliott <cpan@browsing.co.uk>
442
443 Roman Filippov
444
445 Lance Brown <lance@bearcircle.net>
446
447 Devin Austin <dhoss@cpan.org>
448
449 Chris Nehren <apeiron@cpan.org>
450
451 =head1 COPYRIGHT
452
453 Copyright (c) 2007 - 2009
454 the Catalyst::View::Email L</AUTHORS> and L</CONTRIBUTORS>
455 as listed above.
456
457 =head1 LICENSE
458
459 This library is free software, you can redistribute it and/or modify it under
460 the same terms as Perl itself.
461
462 =cut
463
464 1;