From: J. Shirley Date: Sat, 12 May 2007 19:10:40 +0000 (+0000) Subject: Initial checkin of Catalyst::View::Email - suitable for developers to poke X-Git-Tag: v0.14~46 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=529915aba4329566d2214e5582806a0f84f7ac02;p=catagits%2FCatalyst-View-Email.git Initial checkin of Catalyst::View::Email - suitable for developers to poke --- 529915aba4329566d2214e5582806a0f84f7ac02 diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..5435eb8 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,7 @@ +lib/Catalyst/Helper/View/Email.pm +lib/Catalyst/Helper/View/Email/Template.pm +lib/Catalyst/View/Email.pm +lib/Catalyst/View/Email/Template.pm +Makefile.PL +MANIFEST This list of files +META.yml diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP new file mode 100644 index 0000000..10378bb --- /dev/null +++ b/MANIFEST.SKIP @@ -0,0 +1,41 @@ +# Avoid version control files. +\bRCS\b +\bCVS\b +,v$ +\B\.svn\b + +# Avoid Makemaker generated and utility files. +\bMakefile$ +\bblib +\bMakeMaker-\d +\bpm_to_blib$ +\bblibdirs$ +^MANIFEST\.SKIP$ + +# Avoid Module::Build generated and utility files. +\bBuild$ +\b_build + +# Avoid temp and backup files. +~$ +\.tmp$ +\.old$ +\.bak$ +\#$ +\b\.# +\.DS_Store$ + +# Avoid Apache::Test files +t/conf/apache_test_config.pm +t/conf/extra.conf$ +t/conf/httpd.conf +t/conf/mime.types +t/htdocs +t/logs +t/var + +# No tarballs! +\.gz$ + +# Skip the roadmap +lib/Catalyst/ROADMAP.pod diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..1cd2100 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,14 @@ +use inc::Module::Install; + +name 'Catalyst-View-Email'; +all_from 'lib/Catalyst/View/Email.pm'; + +requires 'Catalyst' => '5.7'; +requires 'Class::C3'; + +requires 'Email::Send' => '2.185'; +requires 'Email::MIME' => '1.859'; +requires 'Email::MIME::Creator' => '1.453'; + +auto_install; +WriteAll; diff --git a/lib/Catalyst/Helper/View/Email.pm b/lib/Catalyst/Helper/View/Email.pm new file mode 100644 index 0000000..4e33c99 --- /dev/null +++ b/lib/Catalyst/Helper/View/Email.pm @@ -0,0 +1,84 @@ +package Catalyst::Helper::View::Email; + +use strict; + +=head1 NAME + +Catalyst::Helper::View::Email - Helper for Email Views + +=head1 SYNOPSIS + + script/create.pl view Email Email + +=head1 DESCRIPTION + +Helper for Email Views. + +=head2 METHODS + +=head3 mk_compclass + +=cut + +sub mk_compclass { + my ( $self, $helper ) = @_; + my $file = $helper->{file}; + $helper->render_file( 'compclass', $file ); +} + +=head1 SEE ALSO + +L + +L, L, L, +L, L + +=head1 AUTHOR + +J. Shirley C + +=head1 LICENSE + +This library is free software . You can redistribute it and/or modify +it under the same terms as perl itself. + +=cut + +1; + +__DATA__ + +__compclass__ +package [% class %]; + +use strict; +use base 'Catalyst::View::Email'; + +__PACKAGE__->config( + stash_key => 'email' +); + +=head1 NAME + +[% class %] - Email View for [% app %] + +=head1 DESCRIPTION + +View for sending email from [% app %]. + +=head1 AUTHOR + +[% author %] + +=head1 SEE ALSO + +L<[% app %]> + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/Catalyst/Helper/View/Email/Template.pm b/lib/Catalyst/Helper/View/Email/Template.pm new file mode 100644 index 0000000..f913198 --- /dev/null +++ b/lib/Catalyst/Helper/View/Email/Template.pm @@ -0,0 +1,85 @@ +package Catalyst::Helper::View::Email::Template; + +use strict; + +=head1 NAME + +Catalyst::Helper::View::Email::Template - Helper for Templated Email Views + +=head1 SYNOPSIS + + script/create.pl view Template::Email Template::Email + +=head1 DESCRIPTION + +Helper for Template-based Email Views. + +=head2 METHODS + +=head3 mk_compclass + +=cut + +sub mk_compclass { + my ( $self, $helper ) = @_; + my $file = $helper->{file}; + $helper->render_file( 'compclass', $file ); +} + +=head1 SEE ALSO + +L + +L, L, L, +L, L + +=head1 AUTHOR + +J. Shirley C + +=head1 LICENSE + +This library is free software . You can redistribute it and/or modify +it under the same terms as perl itself. + +=cut + +1; + +__DATA__ + +__compclass__ +package [% class %]; + +use strict; +use base 'Catalyst::View::Email::Template'; + +__PACKAGE__->config( + stash_key => 'email', + template_prefix => '' +); + +=head1 NAME + +[% class %] - Templated Email View for [% app %] + +=head1 DESCRIPTION + +View for sending template-generated email from [% app %]. + +=head1 AUTHOR + +[% author %] + +=head1 SEE ALSO + +L<[% app %]> + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/Catalyst/View/Email.pm b/lib/Catalyst/View/Email.pm new file mode 100644 index 0000000..a64b2c0 --- /dev/null +++ b/lib/Catalyst/View/Email.pm @@ -0,0 +1,201 @@ +package Catalyst::View::Email; + +use warnings; +use strict; + +use Carp; + +use Email::Send; +use Email::MIME::Creator; + +use base qw|Catalyst::View|; + +our $VERSION = '0.01'; + +__PACKAGE__->mk_accessors('mailer'); + +=head1 NAME + +Catalyst::View::Email - Send Email from Catalyst + +=head1 SYNOPSIS + +This module simply sends out email from a stash key specified in the +configuration settings. + +=head1 CONFIGURATION + +In your app configuration (example in L): + View::Email: + stash_key: email + content_type: text/plain + sender: + method: SMTP + host: smtp.myhost.com + username: username + password: password + +=cut + +__PACKAGE__->config( + stash_key => 'email', +); + +=head1 SENDING EMAIL + +In your controller, simply forward to the view after populating the C + + sub controller : Private { + my ( $self, $c ) = @_; + $c->stash->{email} = { + to => qq{catalyst@rocksyoursocks.com}, + from => qq{no-reply@socksthatarerocked.com}, + subject => qq{Your Subject Here}, + body => qq{Body Body Body} + }; + $c->forward('View::Email'); + } + +Alternatively, you can use a more raw interface, and specify the headers as +an array reference. + + $c->stash->{email} = { + header => [ + To => 'foo@bar.com', + Subject => 'Note the capitalization differences' + ], + body => qq{Ain't got no body, and nobody cares.}, + # Or, send parts + parts => [ + Email::MIME->create( + attributes => { + content_type => 'text/plain', + disposition => 'attachment', + charset => 'US-ASCII', + }, + body => qq{Got a body, but didn't get ahead.} + ) + ], + }; + +=head1 HANDLING FAILURES + +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('View::Email'); + if ( scalar( @{ $c->error } ) ) { + $c->error(0); # Reset the error condition if you need to + $c->res->body('Oh noes!'); + } else { + $c->res->body('Email sent A-OK! (At least as far as we can tell)'); + } + +=head1 OTHER MAILERS + +Now, it's no fun to just send out email using plain strings. We also have +L for use. You can also toggle this as being used +by setting up your configuration to look like this: + + View::Email: + default: TT + +Then, Catalyst::View::Email will forward to View::Email::TT by default. + +=cut + +sub new { + my ( $class ) = shift; + my $self = $class->next::method(@_); + + my $mailer = Email::Send->new; + + if ( my $method = $self->config->{sender}->{method} ) { + croak "$method is not supported, see Email::Send" + unless $mailer->mailer_available($method); + $mailer->mailer($method); + } else { + # Default case, run through the most likely options first. + for ( qw/SMTP Sendmail Qmail/ ) { + $mailer->mailer($_) and last if $mailer->mailer_available($_); + } + } + + if ( $mailer->mailer eq 'SMTP' ) { + my $host = $self->config->{sender}->{host} || 'localhost'; + $mailer->mailer_args([ Host => $host ]); + } + + $self->mailer($mailer); + + return $self; +} + +sub process { + my ( $self, $c ) = @_; + + croak "Unable to send mail, bad mail configuration" + unless $self->mailer; + + my $email = $c->stash->{$self->config->{stash_key}}; + croak "Can't send email without a valid email structure" + unless $email; + + if ( $self->config->{content_type} ) { + $email->{content_type} ||= $self->config->{content_type}; + } + + my $header = $email->{header} || []; + push @$header, ('To' => delete $email->{to}) + if $email->{to}; + 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 $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 ); + + if ( $parts and ref $parts eq 'ARRAY' ) { + $mime{parts} = $parts; + } else { + $mime{body} = $body; + } + + my $message = Email::MIME->create(%mime); + + if ( $message ) { + $self->mailer->send($message); + } else { + croak "Unable to create message"; + } +} + +=head1 SEE ALSO + +=head2 L - Send fancy template emails with Cat + +=head2 L - The Catalyst Manual + +=head2 L - The Catalyst Cookbook + +=head1 AUTHORS + +J. Shirley + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify it under +the same terms as Perl itself. + +=cut + +1; diff --git a/lib/Catalyst/View/Email/Template.pm b/lib/Catalyst/View/Email/Template.pm new file mode 100644 index 0000000..5a35783 --- /dev/null +++ b/lib/Catalyst/View/Email/Template.pm @@ -0,0 +1,182 @@ +package Catalyst::View::Email::Template; + +use warnings; +use strict; + +use Class::C3; +use Carp; + +use Email::MIME::Creator; + +use base qw|Catalyst::View::Email|; + +our $VERSION = '0.01'; + +=head1 NAME + +Catalyst::View::Email::Template - Send Templated Email from Catalyst + +=head1 SYNOPSIS + +Sends Templated mail, based upon your Default View. Will capture the output +of the rendering path, slurps in based on mime-types and assembles a multi-part +email and sends it out. + +=head2 CONFIGURATION + + View::Email::Template: + # Set it up so if you have multiple parts, they're alternatives. + # This is on the top-level message, not the individual parts. + content_type: multipart/alternative + # Optional prefix to look somewhere under the existing configured + # template paths. + template_prefix: email + # Where to look in the stash for the email information + stash_key: email + # Setup how to send the email + sender: + method: SMTP + host: smtp.myhost.com + username: username + password: password + +=head1 SENDING EMAIL + +Sending email is just setting up your stash key, and forwarding to the view. + + $c->stash->{email} = { + to => 'jshirley@gmail.com', + from => 'no-reply@foobar.com', + subject => 'I am a Catalyst generated email', + # Specify which templates to include + templates => [ + qw{text_plain/test.tt}, + qw{text_html/test.tt} + ] + }; + $c->forward('View::Email::Template'); + +If it fails $c->error will have the error message. + +=cut + +__PACKAGE__->config( + template_prefix => '', +); + + +# This view hitches into your default view and will call the render function +# on the templates provided. This means that you have a layer of abstraction +# and you aren't required to modify your templates based on your desired engine +# (Template Toolkit or Mason, for example). As long as the view adequately +# supports ->render, all things are good. Mason, and others, are not good. + +# +# The path here is to check configuration for the template root, and then +# proceed to call render on the subsequent templates and stuff each one +# into an Email::MIME container. The mime-type will be stupidly guessed with +# the subdir on the template. +# +# TODO: Make this unretarded. +# +sub process { + my ( $self, $c ) = @_; + + my $stash_key = $self->config->{stash_key} || 'email'; + + croak "No template specified for rendering" + unless $c->stash->{$stash_key}->{template} or + $c->stash->{$stash_key}->{templates}; + + # Where to look + my $template_prefix = $self->config->{template_prefix}; + my @templates = (); + if ( $c->stash->{$stash_key}->{templates} ) { + push @templates, map { + join('/', $template_prefix, $_); + } @{$c->stash->{$stash_key}->{templates}}; + + } else { + push @templates, join('/', $template_prefix, + $c->stash->{$stash_key}->{template}); + } + + my $default_view = $c->view( $self->config->{default_view} ); + + unless ( $default_view->can('render') ) { + croak "Email::Template's configured view does not have a render method!"; + } + + #$c->log->_dump($default_view->config); + + my @parts = (); + foreach my $template ( @templates ) { + $template =~ s#^/+##; # Make sure that we don't have an absolute path. + # This seems really stupid to me... argh. will give me nightmares! + my $template_path = $template; + $template_path =~ s#^$template_prefix/##; + my ( $content_type, $extra ) = split('/', $template_path); + if ( $extra ) { + $content_type ||= 'text/plain'; + $content_type =~ s#_#/#; + } else { + $content_type = 'text/plain'; + } + my $output = $default_view->render( $c, $template, + { content_type => $content_type }); + # Got a ref, not a scalar. An error! + if ( ref $output ) { + croak $output->can("as_string") ? $output->as_string : $output; + } + push @parts, Email::MIME->create( + attributes => { + content_type => $content_type + }, + body => $output + ); + } + delete $c->stash->{email}->{body}; + $c->stash->{email}->{parts} ||= []; + push @{$c->stash->{email}->{parts}}, @parts; + + # Let C::V::Email do the actual sending. We just assemble the tasty bits. + return $self->next::method($c); +} + +=head1 TODO + +=head2 ATTACHMENTS + +There needs to be a method to support attachments. What I am thinking is +something along these lines: + attachments => [ + # Set the body to a file handle object, specify content_type and + # the file name. (name is what it is sent at, not the file) + { body => $fh, name => "foo.pdf", content_type => "application/pdf" }, + # Or, specify a filename that is added, and hey, encoding! + { filename => "foo.gif", name => "foo.gif", content_type => "application/pdf", encoding => "quoted-printable" }, + # Or, just a path to a file, and do some guesswork for the content type + "/path/to/somefile.pdf", + ] + +=head1 SEE ALSO + +=head2 L - Send plain boring emails with Catalyst + +=head2 L - The Catalyst Manual + +=head2 L - The Catalyst Cookbook + +=head1 AUTHORS + +J. Shirley + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify it under +the same terms as Perl itself. + +=cut + +1; +