73bc44f6ee469dbda3789bb42181c3e5d399b84c
[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 Email::Send;
10 use Email::MIME::Creator;
11
12 use base qw/ Catalyst::View /;
13
14 our $VERSION = '0.09999_02';
15
16 __PACKAGE__->mk_accessors(qw/ mailer /);
17
18 =head1 NAME
19
20 Catalyst::View::Email - Send Email from Catalyst
21
22 =head1 SYNOPSIS
23
24 This module simply sends out email from a stash key specified in the
25 configuration settings.
26
27 =head1 CONFIGURATION
28
29 Use the helper to create your View:
30     
31     $ script/myapp_create.pl view Email Email
32
33 In your app configuration (example in L<YAML>):
34
35     View::Email:
36         # Where to look in the stash for the email information.
37         # 'email' is the default, so you don't have to specify it.
38         stash_key: email
39         # Define the defaults for the mail
40         default:
41             # Defines the default content type (mime type).
42             # mandatory
43             content_type: text/plain
44             # Defines the default charset for every MIME part with the content
45             # type text.
46             # According to RFC2049 a MIME part without a charset should
47             # be treated as US-ASCII by the mail client.
48             # If the charset is not set it won't be set for all MIME parts
49             # without an overridden one.
50             # Default: none
51             charset: utf-8
52         # Setup how to send the email
53         # all those options are passed directly to Email::Send
54         sender:
55             mailer: SMTP
56             # mailer_args is passed directly into Email::Send 
57             mailer_args:
58                 Host:       smtp.example.com # defaults to localhost
59                 username:   username
60                 password:   password
61
62 =head2 NOTE ON SMTP
63
64 If you use SMTP and don't specify Host, it will default to localhost and
65 attempt delivery.  This often times means an email will sit in a queue
66 somewhere and not be delivered.
67
68 =cut
69
70 __PACKAGE__->config(
71     stash_key   => 'email',
72     default     => {
73         content_type    => 'text/html',
74     },
75 );
76
77 =head1 SENDING EMAIL
78
79 In your controller, simply forward to the view after populating the C<stash_key>
80
81     sub controller : Private {
82         my ( $self, $c ) = @_;
83         $c->stash->{email} = {
84             to      => q{catalyst@rocksyoursocks.com},
85             cc      => q{foo@bar.com},
86             bcc     => q{hidden@secret.com},
87             from    => q{no-reply@socksthatarerocked.com},
88             subject => qq{Your Subject Here},
89             body    => qq{Body Body Body}
90         };
91         $c->forward( $c->view('Email' ) );
92     }
93
94 Alternatively, you can use a more raw interface, and specify the headers as
95 an array reference.
96
97     $c->stash->{email} = {
98         header => [
99             To      => 'foo@bar.com',
100             Subject => 'Note the capitalization differences'
101         ],
102         body => qq{Ain't got no body, and nobody cares.},
103         # Or, send parts
104         parts => [
105             Email::MIME->create(
106                 attributes => {
107                     content_type => 'text/plain',
108                     disposition  => 'attachment',
109                     charset      => 'US-ASCII',
110                 },
111                 body => qq{Got a body, but didn't get ahead.}
112             )
113         ],
114     };
115
116 =head1 HANDLING ERRORS
117
118 If the email fails to send, the view will die (throw an exception).  After
119 your forward to the view, it is a good idea to check for errors:
120     
121     $c->forward( $c->view('Email' ) );
122     if ( scalar( @{ $c->error } ) ) {
123         $c->error(0); # Reset the error condition if you need to
124         $c->res->body('Oh noes!');
125     } else {
126         $c->res->body('Email sent A-OK! (At least as far as we can tell)');
127     }
128
129 =head1 USING TEMPLATES FOR EMAIL
130
131 Now, it's no fun to just send out email using plain strings.
132 Take a look at L<Catalyst::View::Email::Template> to see how you can use your
133 favourite template engine to render the mail body.
134
135
136 =cut
137
138 sub new {
139     my $self = shift->next::method(@_);
140
141     my ( $c, $arguments ) = @_;
142     
143     my $stash_key = $self->{stash_key};
144     croak "$self stash_key isn't defined!"
145         if ($stash_key eq '');
146
147     my $sender = Email::Send->new;
148
149     if ( my $mailer = $self->{sender}->{mailer} ) {
150         croak "$mailer is not supported, see Email::Send"
151             unless $sender->mailer_available($mailer);
152         $sender->mailer($mailer);
153     } else {
154         # Default case, run through the most likely options first.
155         for ( qw/SMTP Sendmail Qmail/ ) {
156             $sender->mailer($_) and last if $sender->mailer_available($_);
157         }
158     }
159
160     if ( my $args = $self->{sender}->{mailer_args} ) {
161         if ( ref $args eq 'HASH' ) {
162             $sender->mailer_args([ %$args ]);
163         }
164         elsif ( ref $args eq 'ARRAY' ) {
165             $sender->mailer_args($args);
166         } else {
167             croak "Invalid mailer_args specified, check pod for Email::Send!";
168         }
169     }
170
171     $self->mailer($sender);
172
173     return $self;
174 }
175
176 =head2 process
177
178 The process method does the actual processing when the view is dispatched to.
179
180 This method sets up the email parts and hands off to L<Email::Send> to handle
181 the actual email delivery.
182
183 =cut
184
185 sub process {
186     my ( $self, $c ) = @_;
187
188     croak "Unable to send mail, bad mail configuration"
189         unless $self->mailer;
190
191     my $email  = $c->stash->{$self->{stash_key}};
192     croak "Can't send email without a valid email structure"
193         unless $email;
194
195     if ( exists $self->{content_type} ) {
196         $email->{content_type} ||= $self->{content_type};
197     }
198
199     my $header  = $email->{header} || [];
200         push @$header, ('To' => delete $email->{to})
201             if $email->{to};
202         push @$header, ('Cc' => delete $email->{cc})
203             if $email->{cc};
204         push @$header, ('Bcc' => delete $email->{bcc})
205             if $email->{bcc};
206         push @$header, ('From' => delete $email->{from})
207             if $email->{from};
208         push @$header, ('Subject' => delete $email->{subject})
209             if $email->{subject};
210         push @$header, ('Content-type' => delete $email->{content_type})
211             if $email->{content_type};
212
213     my $parts = $email->{parts};
214     my $body  = $email->{body};
215    
216     unless ( $parts or $body ) {
217         croak "Can't send email without parts or body, check stash";
218     }
219
220     my %mime = ( header => $header );
221
222     if ( $parts and ref $parts eq 'ARRAY' ) {
223         $mime{parts} = $parts;
224     } else {
225         $mime{body} = $body;
226     }
227     
228     if ( $mime{attributes} and not $mime{attributes}->{charset} and
229          $self->{default}->{charset} )
230     {
231         $mime{attributes}->{charset} = $self->{default}->{charset};
232     }
233
234     my $message = $self->generate_message( $c, \%mime );
235
236     #my $message = Email::MIME->create(%mime);
237
238     if ( $message ) {
239         my $return = $self->mailer->send($message);
240         # return is a Return::Value object, so this will stringify as the error
241         # in the case of a failure.  
242         croak "$return" if !$return;
243     } else {
244         croak "Unable to create message";
245     }
246 }
247
248 =head2 setup_attributes
249
250 Merge attributes with the configured defaults.  You can override this method to
251 return a structure to pass into L<generate_message> which subsequently
252 passes the return value of this method to Email::MIME->create under the
253 C<attributes> key.
254
255 =cut
256
257 sub setup_attributes {
258     my ( $self, $c, $attrs ) = @_;
259     
260     my $default_content_type    = $self->{default}->{content_type};
261     my $default_charset         = $self->{default}->{charset};
262
263     my $e_m_attrs = {};
264
265     if (exists $attrs->{content_type} && defined $attrs->{content_type} && $attrs->{content_type} ne '') {
266         $c->log->debug('C::V::Email uses specified content_type ' . $attrs->{content_type} . '.') if $c->debug;
267         $e_m_attrs->{content_type} = $attrs->{content_type};
268     }
269     elsif (defined $default_content_type && $default_content_type ne '') {
270         $c->log->debug("C::V::Email uses default content_type $default_content_type.") if $c->debug;
271         $e_m_attrs->{content_type} = $default_content_type;
272     }
273    
274     if (exists $attrs->{charset} && defined $attrs->{charset} && $attrs->{charset} ne '') {
275         $e_m_attrs->{charset} = $attrs->{charset};
276     }
277     elsif (defined $default_charset && $default_charset ne '') {
278         $e_m_attrs->{charset} = $default_charset;
279     }
280
281     return $e_m_attrs;
282 }
283
284 =head2 generate_message($c, $attr)
285
286 Generate a message part, which should be an L<Email::MIME> object and return it.
287
288 Takes the attributes, merges with the defaults as necessary and returns a
289 message object.
290
291 =cut
292
293 sub generate_message {
294     my ( $self, $c, $attr ) = @_;
295
296     # setup the attributes (merge with defaults)
297     $attr->{attributes} = $self->setup_attributes($c, $attr->{attributes});
298     return Email::MIME->create(%$attr);
299 }
300
301 =head1 SEE ALSO
302
303 =head2 L<Catalyst::View::Email::Template> - Send fancy template emails with Cat
304
305 =head2 L<Catalyst::Manual> - The Catalyst Manual
306
307 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
308
309 =head1 AUTHORS
310
311 J. Shirley <jshirley@gmail.com>
312
313 =head1 CONTRIBUTORS
314
315 (Thanks!)
316
317 Matt S Trout
318
319 Daniel Westermann-Clark
320
321 Simon Elliott <cpan@browsing.co.uk>
322
323 Roman Filippov
324
325 Alexander Hartmaier <alex_hartmaier@hotmail.com>
326
327 =head1 LICENSE
328
329 This library is free software, you can redistribute it and/or modify it under
330 the same terms as Perl itself.
331
332 =cut
333
334 1;