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