X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst%2FView%2FEmail.pm;h=f7605e8b3263cda0e6b2409be2d16ce1e64a82de;hb=5d205d68e1cd576075c734a6d9ce2a286cc0d607;hp=73bc44f6ee469dbda3789bb42181c3e5d399b84c;hpb=2a175229402f4ffd8d80da5ca80a5e231354c653;p=catagits%2FCatalyst-View-Email.git diff --git a/lib/Catalyst/View/Email.pm b/lib/Catalyst/View/Email.pm index 73bc44f..f7605e8 100644 --- a/lib/Catalyst/View/Email.pm +++ b/lib/Catalyst/View/Email.pm @@ -1,19 +1,57 @@ package Catalyst::View::Email; -use warnings; -use strict; - -use Class::C3; +use Moose; use Carp; -use Email::Send; +use Encode qw(encode decode); +use Email::Sender::Simple qw/ sendmail /; use Email::MIME::Creator; +extends 'Catalyst::View'; + +our $VERSION = '0.28'; +$VERSION = eval $VERSION; -use base qw/ Catalyst::View /; +has 'mailer' => ( + is => 'rw', + isa => 'Str', + lazy => 1, + default => sub { "sendmail" } +); -our $VERSION = '0.09999_02'; +has '_mailer_obj' => ( + is => 'rw', + isa => 'Email::Sender::Transport', + lazy => 1, + builder => '_build_mailer_obj', +); -__PACKAGE__->mk_accessors(qw/ mailer /); +has 'stash_key' => ( + is => 'rw', + isa => 'Str', + lazy => 1, + default => sub { "email" } +); + +has 'default' => ( + is => 'rw', + isa => 'HashRef', + default => sub { { content_type => 'text/plain' } }, + lazy => 1, +); + +has 'sender' => ( + is => 'rw', + isa => 'HashRef', + lazy => 1, + default => sub { { mailer => shift->mailer } } +); + +has 'content_type' => ( + is => 'rw', + isa => 'Str', + default => sub { shift->default->{content_type} }, + lazy => 1, +); =head1 NAME @@ -21,83 +59,94 @@ Catalyst::View::Email - Send Email from Catalyst =head1 SYNOPSIS -This module simply sends out email from a stash key specified in the +This module sends out emails from a stash key specified in the configuration settings. =head1 CONFIGURATION +WARNING: since version 0.10 the configuration options slightly changed! + Use the helper to create your View: $ script/myapp_create.pl view Email Email -In your app configuration (example in L): - - View::Email: - # Where to look in the stash for the email information. - # 'email' is the default, so you don't have to specify it. - stash_key: email - # Define the defaults for the mail - default: - # Defines the default content type (mime type). - # mandatory - content_type: text/plain - # Defines the default charset for every MIME part with the content - # type text. - # According to RFC2049 a MIME part without a charset should - # be treated as US-ASCII by the mail client. - # If the charset is not set it won't be set for all MIME parts - # without an overridden one. - # Default: none - charset: utf-8 - # Setup how to send the email - # all those options are passed directly to Email::Send - sender: - mailer: SMTP - # mailer_args is passed directly into Email::Send - mailer_args: - Host: smtp.example.com # defaults to localhost - username: username - password: password - -=head2 NOTE ON SMTP - -If you use SMTP and don't specify Host, it will default to localhost and -attempt delivery. This often times means an email will sit in a queue -somewhere and not be delivered. +In your app configuration: + + __PACKAGE__->config( + 'View::Email' => { + # Where to look in the stash for the email information. + # 'email' is the default, so you don't have to specify it. + stash_key => 'email', + # Define the defaults for the mail + default => { + # Defines the default content type (mime type). Mandatory + content_type => 'text/plain', + # Defines the default charset for every MIME part with the + # content type text. + # According to RFC2049 a MIME part without a charset should + # be treated as US-ASCII by the mail client. + # If the charset is not set it won't be set for all MIME parts + # without an overridden one. + # Default: none + charset => 'utf-8' + }, + # Setup how to send the email + # all those options are passed directly to Email::Sender::Simple + sender => { + # if mailer doesn't start with Email::Sender::Simple::Transport::, + # then this is prepended. + mailer => 'SMTP', + # mailer_args is passed directly into Email::Sender::Simple + mailer_args => { + Host => 'smtp.example.com', # defaults to localhost + username => 'username', + password => 'password', + } + } + } + ); + +=head1 NOTE ON SMTP -=cut +If you use SMTP and don't specify host, it will default to localhost and +attempt delivery. This often means an email will sit in a queue and +not be delivered. -__PACKAGE__->config( - stash_key => 'email', - default => { - content_type => 'text/html', - }, -); +=cut =head1 SENDING EMAIL -In your controller, simply forward to the view after populating the C +Sending email is just filling the stash and forwarding to the view: sub controller : Private { my ( $self, $c ) = @_; + $c->stash->{email} = { - to => q{catalyst@rocksyoursocks.com}, - cc => q{foo@bar.com}, - bcc => q{hidden@secret.com}, - from => q{no-reply@socksthatarerocked.com}, - subject => qq{Your Subject Here}, - body => qq{Body Body Body} + to => 'jshirley@gmail.com', + cc => 'abraxxa@cpan.org', + bcc => join ',', qw/hidden@secret.com hidden2@foobar.com/, + from => 'no-reply@foobar.com', + subject => 'I am a Catalyst generated email', + body => 'Body Body Body', }; - $c->forward( $c->view('Email' ) ); + + $c->forward( $c->view('Email') ); } -Alternatively, you can use a more raw interface, and specify the headers as -an array reference. +Alternatively you can use a more raw interface and specify the headers as +an array reference like it is passed to L. +Note that you may also mix both syntaxes if you like ours better but need to +specify additional header attributes. +The attributes are appended to the header array reference without overwriting +contained ones. $c->stash->{email} = { header => [ - To => 'foo@bar.com', - Subject => 'Note the capitalization differences' + To => 'jshirley@gmail.com', + Cc => 'abraxxa@cpan.org', + Bcc => join ',', qw/hidden@secret.com hidden2@foobar.com/, + From => 'no-reply@foobar.com', + Subject => 'Note the capitalization differences', ], body => qq{Ain't got no body, and nobody cares.}, # Or, send parts @@ -108,22 +157,23 @@ an array reference. disposition => 'attachment', charset => 'US-ASCII', }, - body => qq{Got a body, but didn't get ahead.} + body => qq{Got a body, but didn't get ahead.}, ) ], }; =head1 HANDLING ERRORS -If the email fails to send, the view will die (throw an exception). After -your forward to the view, it is a good idea to check for errors: +If the email fails to send, the view will die (throw an exception). +After your forward to the view, it is a good idea to check for errors: + + $c->forward( $c->view('Email') ); - $c->forward( $c->view('Email' ) ); if ( scalar( @{ $c->error } ) ) { $c->error(0); # Reset the error condition if you need to - $c->res->body('Oh noes!'); + $c->response->body('Oh noes!'); } else { - $c->res->body('Email sent A-OK! (At least as far as we can tell)'); + $c->response->body('Email sent A-OK! (At least as far as we can tell)'); } =head1 USING TEMPLATES FOR EMAIL @@ -132,52 +182,45 @@ Now, it's no fun to just send out email using plain strings. Take a look at L to see how you can use your favourite template engine to render the mail body. +=head1 METHODS + +=over 4 + +=item new + +Validates the base config and creates the L object for later use +by process. =cut -sub new { - my $self = shift->next::method(@_); +sub BUILD { + my $self = shift; - my ( $c, $arguments ) = @_; - - my $stash_key = $self->{stash_key}; + my $stash_key = $self->stash_key; croak "$self stash_key isn't defined!" - if ($stash_key eq ''); + if ( $stash_key eq '' ); - my $sender = Email::Send->new; +} - if ( my $mailer = $self->{sender}->{mailer} ) { - croak "$mailer is not supported, see Email::Send" - unless $sender->mailer_available($mailer); - $sender->mailer($mailer); - } else { - # Default case, run through the most likely options first. - for ( qw/SMTP Sendmail Qmail/ ) { - $sender->mailer($_) and last if $sender->mailer_available($_); - } - } +sub _build_mailer_obj { + my ($self) = @_; + my $transport_class = ucfirst $self->sender->{mailer}; - if ( my $args = $self->{sender}->{mailer_args} ) { - if ( ref $args eq 'HASH' ) { - $sender->mailer_args([ %$args ]); - } - elsif ( ref $args eq 'ARRAY' ) { - $sender->mailer_args($args); - } else { - croak "Invalid mailer_args specified, check pod for Email::Send!"; - } + # borrowed from Email::Sender::Simple -- apeiron, 2010-01-26 + if ( $transport_class !~ /^Email::Sender::Transport::/ ) { + $transport_class = "Email::Sender::Transport::$transport_class"; } - $self->mailer($sender); + Class::MOP::load_class($transport_class); - return $self; + return $transport_class->new( $self->sender->{mailer_args} || {} ); } -=head2 process +=item process($c) The process method does the actual processing when the view is dispatched to. -This method sets up the email parts and hands off to L to handle +This method sets up the email parts and hands off to L to handle the actual email delivery. =cut @@ -186,68 +229,74 @@ sub process { my ( $self, $c ) = @_; croak "Unable to send mail, bad mail configuration" - unless $self->mailer; + unless $self->sender->{mailer}; - my $email = $c->stash->{$self->{stash_key}}; + my $email = $c->stash->{ $self->stash_key }; croak "Can't send email without a valid email structure" - unless $email; + unless $email; - if ( exists $self->{content_type} ) { - $email->{content_type} ||= $self->{content_type}; + # Default content type + if ( $self->content_type and not $email->{content_type} ) { + $email->{content_type} = $self->content_type; } - my $header = $email->{header} || []; - push @$header, ('To' => delete $email->{to}) - if $email->{to}; - push @$header, ('Cc' => delete $email->{cc}) - if $email->{cc}; - push @$header, ('Bcc' => delete $email->{bcc}) - if $email->{bcc}; - push @$header, ('From' => delete $email->{from}) - if $email->{from}; - push @$header, ('Subject' => delete $email->{subject}) - if $email->{subject}; - push @$header, ('Content-type' => delete $email->{content_type}) - if $email->{content_type}; + my $header = $email->{header} || []; + push @$header, ( 'To' => delete $email->{to} ) + if $email->{to}; + push @$header, ( 'Cc' => delete $email->{cc} ) + if $email->{cc}; + push @$header, ( 'Bcc' => delete $email->{bcc} ) + if $email->{bcc}; + push @$header, ( 'From' => delete $email->{from} ) + if $email->{from}; + push @$header, + ( 'Subject' => Encode::encode( 'MIME-Header', delete $email->{subject} ) ) + if $email->{subject}; + push @$header, ( 'Content-type' => $email->{content_type} ) + if $email->{content_type}; my $parts = $email->{parts}; my $body = $email->{body}; - + unless ( $parts or $body ) { croak "Can't send email without parts or body, check stash"; } - my %mime = ( header => $header ); + my %mime = ( header => $header, attributes => {} ); if ( $parts and ref $parts eq 'ARRAY' ) { $mime{parts} = $parts; - } else { + } + else { $mime{body} = $body; } - - if ( $mime{attributes} and not $mime{attributes}->{charset} and - $self->{default}->{charset} ) + + $mime{attributes}->{content_type} = $email->{content_type} + if $email->{content_type}; + if ( $mime{attributes} + and not $mime{attributes}->{charset} + and $self->{default}->{charset} ) { $mime{attributes}->{charset} = $self->{default}->{charset}; } my $message = $self->generate_message( $c, \%mime ); - #my $message = Email::MIME->create(%mime); + if ($message) { + my $return = sendmail( $message, { transport => $self->_mailer_obj } ); - if ( $message ) { - my $return = $self->mailer->send($message); # return is a Return::Value object, so this will stringify as the error - # in the case of a failure. + # in the case of a failure. croak "$return" if !$return; - } else { + } + else { croak "Unable to create message"; } } -=head2 setup_attributes +=item setup_attributes($c, $attr) -Merge attributes with the configured defaults. You can override this method to +Merge attributes with the configured defaults. You can override this method to return a structure to pass into L which subsequently passes the return value of this method to Email::MIME->create under the C key. @@ -256,32 +305,43 @@ C key. sub setup_attributes { my ( $self, $c, $attrs ) = @_; - - my $default_content_type = $self->{default}->{content_type}; - my $default_charset = $self->{default}->{charset}; + + my $default_content_type = $self->default->{content_type}; + my $default_charset = $self->default->{charset}; my $e_m_attrs = {}; - if (exists $attrs->{content_type} && defined $attrs->{content_type} && $attrs->{content_type} ne '') { - $c->log->debug('C::V::Email uses specified content_type ' . $attrs->{content_type} . '.') if $c->debug; + if ( exists $attrs->{content_type} + && defined $attrs->{content_type} + && $attrs->{content_type} ne '' ) + { + $c->log->debug( 'C::V::Email uses specified content_type ' + . $attrs->{content_type} + . '.' ) + if $c->debug; $e_m_attrs->{content_type} = $attrs->{content_type}; } - elsif (defined $default_content_type && $default_content_type ne '') { - $c->log->debug("C::V::Email uses default content_type $default_content_type.") if $c->debug; + elsif ( defined $default_content_type && $default_content_type ne '' ) { + $c->log->debug( + "C::V::Email uses default content_type $default_content_type.") + if $c->debug; $e_m_attrs->{content_type} = $default_content_type; } - - if (exists $attrs->{charset} && defined $attrs->{charset} && $attrs->{charset} ne '') { + + if ( exists $attrs->{charset} + && defined $attrs->{charset} + && $attrs->{charset} ne '' ) + { $e_m_attrs->{charset} = $attrs->{charset}; } - elsif (defined $default_charset && $default_charset ne '') { + elsif ( defined $default_charset && $default_charset ne '' ) { $e_m_attrs->{charset} = $default_charset; } return $e_m_attrs; } -=head2 generate_message($c, $attr) +=item generate_message($c, $attr) Generate a message part, which should be an L object and return it. @@ -293,11 +353,32 @@ message object. sub generate_message { my ( $self, $c, $attr ) = @_; - # setup the attributes (merge with defaults) - $attr->{attributes} = $self->setup_attributes($c, $attr->{attributes}); - return Email::MIME->create(%$attr); + # setup the attributes (merge with defaultis) + $attr->{attributes} = $self->setup_attributes( $c, $attr->{attributes} ); + Email::MIME->create( %$attr ); } +=back + + +=head1 TROUBLESHOOTING + +As with most things computer related, things break. Email even more so. +Typically any errors are going to come from using SMTP as your sending method, +which means that if you are having trouble the first place to look is at +L. This module is just a wrapper for L, +so if you get an error on sending, it is likely from there anyway. + +If you are using SMTP and have troubles sending, whether it is authentication +or a very bland "Can't send" message, make sure that you have L and, +if applicable, L installed. + +It is very simple to check that you can connect via L, and if you +do have sending errors the first thing to do is to write a simple script +that attempts to connect. If it works, it is probably something in your +configuration so double check there. If it doesn't, well, keep modifying +the script and/or your mail server configuration until it does! + =head1 SEE ALSO =head2 L - Send fancy template emails with Cat @@ -310,6 +391,8 @@ sub generate_message { J. Shirley +Alexander Hartmaier + =head1 CONTRIBUTORS (Thanks!) @@ -322,7 +405,17 @@ Simon Elliott Roman Filippov -Alexander Hartmaier +Lance Brown + +Devin Austin + +Chris Nehren + +=head1 COPYRIGHT + +Copyright (c) 2007 - 2009 +the Catalyst::View::Email L and L +as listed above. =head1 LICENSE