File Coverage

blib/lib/Catalyst/View/Email.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


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