initial commit
[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.31';
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     isa     => '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                     username => 'username',
103                     password => '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             bcc     => join ',', qw/hidden@secret.com hidden2@foobar.com/,
128             from    => 'no-reply@foobar.com',
129             subject => 'I am a Catalyst generated email',
130             body    => 'Body Body Body',
131         };
132         
133         $c->forward( $c->view('Email') );
134     }
135
136 Alternatively you can use a more raw interface and specify the headers as
137 an array reference like it is passed to L<Email::MIME::Creator>.
138 Note that you may also mix both syntaxes if you like ours better but need to
139 specify additional header attributes.
140 The attributes are appended to the header array reference without overwriting
141 contained ones.
142
143     $c->stash->{email} = {
144         header => [
145             To      => 'jshirley@gmail.com',
146             Cc      => 'abraxxa@cpan.org',
147             Bcc     => join ',', qw/hidden@secret.com hidden2@foobar.com/,
148             From    => 'no-reply@foobar.com',
149             Subject => 'Note the capitalization differences',
150         ],
151         body => qq{Ain't got no body, and nobody cares.},
152         # Or, send parts
153         parts => [
154             Email::MIME->create(
155                 attributes => {
156                     content_type => 'text/plain',
157                     disposition  => 'attachment',
158                     charset      => 'US-ASCII',
159                 },
160                 body => qq{Got a body, but didn't get ahead.},
161             )
162         ],
163     };
164
165 =head1 HANDLING ERRORS
166
167 If the email fails to send, the view will die (throw an exception).
168 After your forward to the view, it is a good idea to check for errors:
169     
170     $c->forward( $c->view('Email') );
171     
172     if ( scalar( @{ $c->error } ) ) {
173         $c->error(0); # Reset the error condition if you need to
174         $c->response->body('Oh noes!');
175     } else {
176         $c->response->body('Email sent A-OK! (At least as far as we can tell)');
177     }
178
179 =head1 USING TEMPLATES FOR EMAIL
180
181 Now, it's no fun to just send out email using plain strings.
182 Take a look at L<Catalyst::View::Email::Template> to see how you can use your
183 favourite template engine to render the mail body.
184
185 =head1 METHODS
186
187 =over 4
188
189 =item new
190
191 Validates the base config and creates the L<Email::Sender::Simple> object for later use
192 by process.
193
194 =cut
195
196 sub BUILD {
197     my $self = shift;
198
199     my $stash_key = $self->stash_key;
200     croak "$self stash_key isn't defined!"
201       if ( $stash_key eq '' );
202
203 }
204
205 sub _build_mailer_obj {
206     my ($self) = @_;
207     my $transport_class = ucfirst $self->sender->{mailer};
208
209     # borrowed from Email::Sender::Simple -- apeiron, 2010-01-26
210     if ( $transport_class !~ /^Email::Sender::Transport::/ ) {
211         $transport_class = "Email::Sender::Transport::$transport_class";
212     }
213
214     Class::MOP::load_class($transport_class);
215
216     return $transport_class->new( $self->sender->{mailer_args} || {} );
217 }
218
219 =item process($c)
220
221 The process method does the actual processing when the view is dispatched to.
222
223 This method sets up the email parts and hands off to L<Email::Sender::Simple> to handle
224 the actual email delivery.
225
226 =cut
227
228 sub process {
229     my ( $self, $c ) = @_;
230
231     croak "Unable to send mail, bad mail configuration"
232       unless $self->sender->{mailer};
233
234     my $email = $c->stash->{ $self->stash_key };
235     croak "Can't send email without a valid email structure"
236       unless $email;
237
238     # Default content type
239     if ( $self->content_type and not $email->{content_type} ) {
240         $email->{content_type} = $self->content_type;
241     }
242
243     my $header = $email->{header} || [];
244     push @$header, ( 'To' => delete $email->{to} )
245       if $email->{to};
246     push @$header, ( 'Cc' => delete $email->{cc} )
247       if $email->{cc};
248     push @$header, ( 'Bcc' => delete $email->{bcc} )
249       if $email->{bcc};
250     push @$header, ( 'From' => delete $email->{from} )
251       if $email->{from};
252     push @$header,
253       ( 'Subject' => Encode::encode( 'MIME-Header', delete $email->{subject} ) )
254       if $email->{subject};
255     push @$header, ( 'Content-type' => $email->{content_type} )
256       if $email->{content_type};
257
258     my $parts = $email->{parts};
259     my $body  = $email->{body};
260
261     unless ( $parts or $body ) {
262         croak "Can't send email without parts or body, check stash";
263     }
264
265     my %mime = ( header => $header, attributes => {} );
266
267     if ( $parts and ref $parts eq 'ARRAY' ) {
268         $mime{parts} = $parts;
269     }
270     else {
271         $mime{body} = $body;
272     }
273
274     $mime{attributes}->{content_type} = $email->{content_type}
275       if $email->{content_type};
276     if (    $mime{attributes}
277         and not $mime{attributes}->{charset}
278         and $self->{default}->{charset} )
279     {
280         $mime{attributes}->{charset} = $self->{default}->{charset};
281     }
282
283     $mime{attributes}->{encoding} = $email->{encoding} 
284         if $email->{encoding};
285
286     my $message = $self->generate_message( $c, \%mime );
287
288     if ($message) {
289         my $return = sendmail( $message, { transport => $self->_mailer_obj } );
290
291         # return is a Return::Value object, so this will stringify as the error
292         # in the case of a failure.
293         croak "$return" if !$return;
294     }
295     else {
296         croak "Unable to create message";
297     }
298 }
299
300 =item setup_attributes($c, $attr)
301
302 Merge attributes with the configured defaults. You can override this method to
303 return a structure to pass into L<generate_message> which subsequently
304 passes the return value of this method to Email::MIME->create under the
305 C<attributes> key.
306
307 =cut
308
309 sub setup_attributes {
310     my ( $self, $c, $attrs ) = @_;
311
312     my $default_content_type = $self->default->{content_type};
313     my $default_charset      = $self->default->{charset}; 
314     my $default_encoding     = $self->default->{encoding};
315
316     my $e_m_attrs = {};
317
318     if (   exists $attrs->{content_type}
319         && defined $attrs->{content_type}
320         && $attrs->{content_type} ne '' )
321     {
322         $c->log->debug( 'C::V::Email uses specified content_type '
323               . $attrs->{content_type}
324               . '.' )
325           if $c->debug;
326         $e_m_attrs->{content_type} = $attrs->{content_type};
327     }
328     elsif ( defined $default_content_type && $default_content_type ne '' ) {
329         $c->log->debug(
330             "C::V::Email uses default content_type $default_content_type.")
331           if $c->debug;
332         $e_m_attrs->{content_type} = $default_content_type;
333     }
334
335     if (   exists $attrs->{charset}
336         && defined $attrs->{charset}
337         && $attrs->{charset} ne '' )
338     {
339         $e_m_attrs->{charset} = $attrs->{charset};
340     }
341     elsif ( defined $default_charset && $default_charset ne '' ) {
342         $e_m_attrs->{charset} = $default_charset;
343     }
344
345     if ( exists $attrs->{encoding}
346          && defined $attrs->{encoding}
347          && $attrs->{encoding} ne '' )
348     {
349         $c->log->debug(
350         'C::V::Email uses specified encoding '
351         . $attrs->{encoding}
352         . '.' )
353          if $c->debug;
354          $e_m_attrs->{encoding} = $attrs->{encoding};
355     }
356      elsif ( defined $default_encoding && $default_encoding ne '' ) {
357          $c->log->debug(
358          "C::V::Email uses default encoding $default_encoding.")
359          if $c->debug;
360          $e_m_attrs->{encoding} = $default_encoding;
361      }
362
363     return $e_m_attrs;
364 }
365
366 =item generate_message($c, $attr)
367
368 Generate a message part, which should be an L<Email::MIME> object and return it.
369
370 Takes the attributes, merges with the defaults as necessary and returns a
371 message object.
372
373 =cut
374
375 sub generate_message {
376     my ( $self, $c, $attr ) = @_;
377
378     # setup the attributes (merge with defaultis)
379     $attr->{attributes} = $self->setup_attributes( $c, $attr->{attributes} );
380     Email::MIME->create( %$attr );
381 }
382
383 =back
384
385
386 =head1 TROUBLESHOOTING
387
388 As with most things computer related, things break.  Email even more so.  
389 Typically any errors are going to come from using SMTP as your sending method,
390 which means that if you are having trouble the first place to look is at
391 L<Email::Sender::Transport::SMTP>.  This module is just a wrapper for L<Email::Sender::Simple>,
392 so if you get an error on sending, it is likely from there anyway.
393
394 If you are using SMTP and have troubles sending, whether it is authentication
395 or a very bland "Can't send" message, make sure that you have L<Net::SMTP> and,
396 if applicable, L<Net::SMTP::SSL> installed.
397
398 It is very simple to check that you can connect via L<Net::SMTP>, and if you
399 do have sending errors the first thing to do is to write a simple script
400 that attempts to connect.  If it works, it is probably something in your
401 configuration so double check there.  If it doesn't, well, keep modifying
402 the script and/or your mail server configuration until it does!
403
404 =head1 SEE ALSO
405
406 =head2 L<Catalyst::View::Email::Template> - Send fancy template emails with Cat
407
408 =head2 L<Catalyst::Manual> - The Catalyst Manual
409
410 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
411
412 =head1 AUTHORS
413
414 J. Shirley <jshirley@gmail.com>
415
416 Alexander Hartmaier <abraxxa@cpan.org>
417
418 =head1 CONTRIBUTORS
419
420 (Thanks!)
421
422 Matt S Trout
423
424 Daniel Westermann-Clark
425
426 Simon Elliott <cpan@browsing.co.uk>
427
428 Roman Filippov
429
430 Lance Brown <lance@bearcircle.net>
431
432 Devin Austin <dhoss@cpan.org>
433
434 Chris Nehren <apeiron@cpan.org>
435
436 =head1 COPYRIGHT
437
438 Copyright (c) 2007 - 2009
439 the Catalyst::View::Email L</AUTHORS> and L</CONTRIBUTORS>
440 as listed above.
441
442 =head1 LICENSE
443
444 This library is free software, you can redistribute it and/or modify it under
445 the same terms as Perl itself.
446
447 =cut
448
449 1;