Minor refactor of the way generate_part works, having the default attribute merging...
[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_01';
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('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 FAILURES
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('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 sub process {
177     my ( $self, $c ) = @_;
178
179     croak "Unable to send mail, bad mail configuration"
180         unless $self->mailer;
181
182     my $email  = $c->stash->{$self->{stash_key}};
183     croak "Can't send email without a valid email structure"
184         unless $email;
185     
186     if ( exists $self->{content_type} ) {
187         $email->{content_type} ||= $self->{content_type};
188     }
189
190     my $header  = $email->{header} || [];
191         push @$header, ('To' => delete $email->{to})
192             if $email->{to};
193         push @$header, ('Cc' => delete $email->{cc})
194             if $email->{cc};
195         push @$header, ('Bcc' => delete $email->{bcc})
196             if $email->{bcc};
197         push @$header, ('From' => delete $email->{from})
198             if $email->{from};
199         push @$header, ('Subject' => delete $email->{subject})
200             if $email->{subject};
201         push @$header, ('Content-type' => delete $email->{content_type})
202             if $email->{content_type};
203
204     my $parts = $email->{parts};
205     my $body  = $email->{body};
206    
207     unless ( $parts or $body ) {
208         croak "Can't send email without parts or body, check stash";
209     }
210
211     my %mime = ( header => $header );
212
213     if ( $parts and ref $parts eq 'ARRAY' ) {
214         $mime{parts} = $parts;
215     } else {
216         $mime{body} = $body;
217     }
218     
219     if ( $mime{attributes} and not $mime{attributes}->{charset} and
220          $self->{default}->{charset} )
221     {
222         $mime{attributes}->{charset} = $self->{default}->{charset};
223     }
224
225     my $message = $self->generate_part( \%mime );
226
227     #my $message = Email::MIME->create(%mime);
228
229     if ( $message ) {
230         my $return = $self->mailer->send($message);
231         croak "$return" if !$return;
232     } else {
233         croak "Unable to create message";
234     }
235 }
236
237 sub setup_attributes {
238     my ( $self, $c, $attrs ) = @_;
239     
240     my $default_content_type    = $self->{default}->{content_type};
241     my $default_charset         = $self->{default}->{charset};
242
243     my $e_m_attrs = {};
244
245     if (exists $attrs->{content_type} && defined $attrs->{content_type} && $attrs->{content_type} ne '') {
246         $e_m_attrs->{content_type} = $attrs->{content_type};
247     }
248     elsif (defined $default_content_type && $default_content_type ne '') {
249         $e_m_attrs->{content_type} = $default_content_type;
250     }
251    
252     if (exists $attrs->{charset} && defined $attrs->{charset} && $attrs->{charset} ne '') {
253         $e_m_attrs->{charset} = $attrs->{charset};
254     }
255     elsif (defined $default_charset && $default_charset ne '') {
256         $e_m_attrs->{charset} = $default_charset;
257     }
258
259     return $e_m_attrs;
260 }
261
262 sub generate_part {
263     my ( $self, $attr ) = @_;
264
265     # setup the attributes (merge with defaults)
266     $attr->{attributes} = $self->setup_attributes($attr->{attributes});
267     return Email::MIME->create(%$attr);
268 }
269
270 =head1 SEE ALSO
271
272 =head2 L<Catalyst::View::Email::Template> - Send fancy template emails with Cat
273
274 =head2 L<Catalyst::Manual> - The Catalyst Manual
275
276 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
277
278 =head1 AUTHORS
279
280 J. Shirley <jshirley@gmail.com>
281
282 =head1 CONTRIBUTORS
283
284 (Thanks!)
285
286 Matt S Trout
287
288 Daniel Westermann-Clark
289
290 Simon Elliott <cpan@browsing.co.uk>
291
292 Roman Filippov
293
294 Alexander Hartmaier <alex_hartmaier@hotmail.com>
295
296 =head1 LICENSE
297
298 This library is free software, you can redistribute it and/or modify it under
299 the same terms as Perl itself.
300
301 =cut
302
303 1;