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