Commit | Line | Data |
529915ab |
1 | package Catalyst::View::Email; |
2 | |
3 | use warnings; |
4 | use strict; |
5 | |
29840e4a |
6 | use Class::C3; |
529915ab |
7 | use Carp; |
8 | |
e512f6ca |
9 | use Encode qw(encode decode); |
529915ab |
10 | use Email::Send; |
11 | use Email::MIME::Creator; |
12 | |
e512f6ca |
13 | use parent 'Catalyst::View'; |
529915ab |
14 | |
b7b30250 |
15 | our $VERSION = '0.13'; |
529915ab |
16 | |
ea115f9b |
17 | __PACKAGE__->mk_accessors(qw/ mailer /); |
529915ab |
18 | |
19 | =head1 NAME |
20 | |
21 | Catalyst::View::Email - Send Email from Catalyst |
22 | |
23 | =head1 SYNOPSIS |
24 | |
4a44bcd3 |
25 | This module sends out emails from a stash key specified in the |
529915ab |
26 | configuration settings. |
27 | |
28 | =head1 CONFIGURATION |
29 | |
4a44bcd3 |
30 | WARNING: since version 0.10 the configuration options slightly changed! |
31 | |
ea115f9b |
32 | Use the helper to create your View: |
33 | |
34 | $ script/myapp_create.pl view Email Email |
35 | |
e512f6ca |
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 | ); |
229f9fdd |
67 | |
4a44bcd3 |
68 | =head1 NOTE ON SMTP |
229f9fdd |
69 | |
70 | If you use SMTP and don't specify Host, it will default to localhost and |
4a44bcd3 |
71 | attempt delivery. This often means an email will sit in a queue and |
72 | not be delivered. |
529915ab |
73 | |
74 | =cut |
75 | |
76 | __PACKAGE__->config( |
06afcdbc |
77 | stash_key => 'email', |
78 | default => { |
4a44bcd3 |
79 | content_type => 'text/plain', |
06afcdbc |
80 | }, |
529915ab |
81 | ); |
82 | |
83 | =head1 SENDING EMAIL |
84 | |
4a44bcd3 |
85 | Sending email is just filling the stash and forwarding to the view: |
529915ab |
86 | |
87 | sub controller : Private { |
88 | my ( $self, $c ) = @_; |
4a44bcd3 |
89 | |
529915ab |
90 | $c->stash->{email} = { |
4a44bcd3 |
91 | to => 'jshirley@gmail.com', |
92 | cc => 'abraxxa@cpan.org', |
b7b30250 |
93 | bcc => 'hidden@secret.com, hidden2@foobar.com', |
4a44bcd3 |
94 | from => 'no-reply@foobar.com', |
95 | subject => 'I am a Catalyst generated email', |
96 | body => 'Body Body Body', |
529915ab |
97 | }; |
4a44bcd3 |
98 | |
99 | $c->forward( $c->view('Email') ); |
529915ab |
100 | } |
101 | |
4a44bcd3 |
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. |
529915ab |
108 | |
109 | $c->stash->{email} = { |
110 | header => [ |
4a44bcd3 |
111 | To => 'jshirley@gmail.com', |
112 | Cc => 'abraxxa@cpan.org', |
b7b30250 |
113 | Bcc => 'hidden@secret.com, hidden2@foobar.com', |
4a44bcd3 |
114 | From => 'no-reply@foobar.com', |
115 | Subject => 'Note the capitalization differences', |
529915ab |
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 | }, |
4a44bcd3 |
126 | body => qq{Got a body, but didn't get ahead.}, |
529915ab |
127 | ) |
128 | ], |
129 | }; |
130 | |
2a175229 |
131 | =head1 HANDLING ERRORS |
529915ab |
132 | |
4a44bcd3 |
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') ); |
529915ab |
137 | |
529915ab |
138 | if ( scalar( @{ $c->error } ) ) { |
139 | $c->error(0); # Reset the error condition if you need to |
4a44bcd3 |
140 | $c->response->body('Oh noes!'); |
529915ab |
141 | } else { |
4a44bcd3 |
142 | $c->response->body('Email sent A-OK! (At least as far as we can tell)'); |
529915ab |
143 | } |
144 | |
ea115f9b |
145 | =head1 USING TEMPLATES FOR EMAIL |
529915ab |
146 | |
ea115f9b |
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. |
529915ab |
150 | |
4a44bcd3 |
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. |
529915ab |
159 | |
160 | =cut |
161 | |
162 | sub new { |
25650747 |
163 | my $self = shift->next::method(@_); |
164 | |
ea115f9b |
165 | my $stash_key = $self->{stash_key}; |
166 | croak "$self stash_key isn't defined!" |
167 | if ($stash_key eq ''); |
529915ab |
168 | |
06afcdbc |
169 | my $sender = Email::Send->new; |
529915ab |
170 | |
06afcdbc |
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); |
4a44bcd3 |
175 | } |
176 | else { |
529915ab |
177 | # Default case, run through the most likely options first. |
178 | for ( qw/SMTP Sendmail Qmail/ ) { |
06afcdbc |
179 | $sender->mailer($_) and last if $sender->mailer_available($_); |
529915ab |
180 | } |
181 | } |
182 | |
06afcdbc |
183 | if ( my $args = $self->{sender}->{mailer_args} ) { |
229f9fdd |
184 | if ( ref $args eq 'HASH' ) { |
06afcdbc |
185 | $sender->mailer_args([ %$args ]); |
229f9fdd |
186 | } |
187 | elsif ( ref $args eq 'ARRAY' ) { |
06afcdbc |
188 | $sender->mailer_args($args); |
229f9fdd |
189 | } else { |
190 | croak "Invalid mailer_args specified, check pod for Email::Send!"; |
191 | } |
529915ab |
192 | } |
193 | |
06afcdbc |
194 | $self->mailer($sender); |
529915ab |
195 | |
196 | return $self; |
197 | } |
198 | |
4a44bcd3 |
199 | =item process($c) |
11a0bf18 |
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 | |
529915ab |
208 | sub process { |
209 | my ( $self, $c ) = @_; |
210 | |
211 | croak "Unable to send mail, bad mail configuration" |
212 | unless $self->mailer; |
213 | |
ea115f9b |
214 | my $email = $c->stash->{$self->{stash_key}}; |
529915ab |
215 | croak "Can't send email without a valid email structure" |
216 | unless $email; |
d0e11256 |
217 | |
ab4326b4 |
218 | # Default content type |
219 | if ( exists $self->{content_type} and not $email->{content_type} ) { |
220 | $email->{content_type} = $self->{content_type}; |
529915ab |
221 | } |
222 | |
223 | my $header = $email->{header} || []; |
224 | push @$header, ('To' => delete $email->{to}) |
225 | if $email->{to}; |
ea115f9b |
226 | push @$header, ('Cc' => delete $email->{cc}) |
227 | if $email->{cc}; |
228 | push @$header, ('Bcc' => delete $email->{bcc}) |
229 | if $email->{bcc}; |
529915ab |
230 | push @$header, ('From' => delete $email->{from}) |
231 | if $email->{from}; |
e512f6ca |
232 | push @$header, ('Subject' => Encode::encode('MIME-Header', delete $email->{subject})) |
529915ab |
233 | if $email->{subject}; |
ab4326b4 |
234 | push @$header, ('Content-type' => $email->{content_type}) |
529915ab |
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 | |
ab4326b4 |
244 | my %mime = ( header => $header, attributes => {} ); |
529915ab |
245 | |
246 | if ( $parts and ref $parts eq 'ARRAY' ) { |
247 | $mime{parts} = $parts; |
248 | } else { |
249 | $mime{body} = $body; |
250 | } |
ab4326b4 |
251 | |
252 | $mime{attributes}->{content_type} = $email->{content_type} |
253 | if $email->{content_type}; |
43090696 |
254 | if ( $mime{attributes} and not $mime{attributes}->{charset} and |
255 | $self->{default}->{charset} ) |
256 | { |
257 | $mime{attributes}->{charset} = $self->{default}->{charset}; |
258 | } |
259 | |
d0e11256 |
260 | my $message = $self->generate_message( $c, \%mime ); |
529915ab |
261 | |
529915ab |
262 | if ( $message ) { |
95629d46 |
263 | my $return = $self->mailer->send($message); |
2a175229 |
264 | # return is a Return::Value object, so this will stringify as the error |
265 | # in the case of a failure. |
95629d46 |
266 | croak "$return" if !$return; |
529915ab |
267 | } else { |
268 | croak "Unable to create message"; |
269 | } |
270 | } |
271 | |
4a44bcd3 |
272 | =item setup_attributes($c, $attr) |
11a0bf18 |
273 | |
4a44bcd3 |
274 | Merge attributes with the configured defaults. You can override this method to |
11a0bf18 |
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 | |
43090696 |
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 '') { |
d0e11256 |
290 | $c->log->debug('C::V::Email uses specified content_type ' . $attrs->{content_type} . '.') if $c->debug; |
43090696 |
291 | $e_m_attrs->{content_type} = $attrs->{content_type}; |
292 | } |
293 | elsif (defined $default_content_type && $default_content_type ne '') { |
d0e11256 |
294 | $c->log->debug("C::V::Email uses default content_type $default_content_type.") if $c->debug; |
43090696 |
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 | |
4a44bcd3 |
308 | =item generate_message($c, $attr) |
11a0bf18 |
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 | |
d0e11256 |
317 | sub generate_message { |
0c8469c1 |
318 | my ( $self, $c, $attr ) = @_; |
43090696 |
319 | |
320 | # setup the attributes (merge with defaults) |
d0e11256 |
321 | $attr->{attributes} = $self->setup_attributes($c, $attr->{attributes}); |
43090696 |
322 | return Email::MIME->create(%$attr); |
323 | } |
324 | |
4a44bcd3 |
325 | =back |
326 | |
e512f6ca |
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 | |
529915ab |
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 | |
4a44bcd3 |
357 | Alexander Hartmaier <abraxxa@cpan.org> |
358 | |
25650747 |
359 | =head1 CONTRIBUTORS |
360 | |
361 | (Thanks!) |
362 | |
94b4e4ff |
363 | Matt S Trout |
364 | |
25650747 |
365 | Daniel Westermann-Clark |
366 | |
ea115f9b |
367 | Simon Elliott <cpan@browsing.co.uk> |
229f9fdd |
368 | |
95629d46 |
369 | Roman Filippov |
370 | |
b7b30250 |
371 | Lance Brown <lance@bearcircle.net> |
372 | |
529915ab |
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; |