File Coverage

blib/lib/Catalyst/View/Email.pm
Criterion Covered Total %
statement 61 69 88.4
branch 21 46 45.6
condition 12 37 32.4
subroutine 10 10 100.0
pod 3 4 75.0
total 107 166 64.4


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