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