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