Updates to mailer_args, pod -- support of hash and array of mailer_args. Version...
[catagits/Catalyst-View-Email.git] / lib / Catalyst / View / Email / Template.pm
1 package Catalyst::View::Email::Template;
2
3 use warnings;
4 use strict;
5
6 use Class::C3;
7 use Carp;
8
9 use Email::MIME::Creator;
10
11 use base qw|Catalyst::View::Email|;
12
13 our $VERSION = '0.03';
14
15 =head1 NAME
16
17 Catalyst::View::Email::Template - Send Templated Email from Catalyst
18
19 =head1 SYNOPSIS
20
21 Sends Templated mail, based upon your Default View.  Will capture the output
22 of the rendering path, slurps in based on mime-types and assembles a multi-part
23 email and sends it out.
24
25 =head2 CONFIGURATION
26
27     View::Email::Template:
28         # Set it up so if you have multiple parts, they're alternatives.
29         # This is on the top-level message, not the individual parts.
30         content_type: multipart/alternative
31         # Optional prefix to look somewhere under the existing configured
32         # template  paths.
33         template_prefix: email
34         # Where to look in the stash for the email information
35         stash_key: email
36         # Setup how to send the email
37         sender:
38             method:     SMTP
39             host:       smtp.myhost.com
40             username:   username
41             password:   password
42
43 =head1 SENDING EMAIL
44
45 Sending email is just setting up your stash key, and forwarding to the view.
46
47     $c->stash->{email} = {
48         to      => 'jshirley@gmail.com',
49         from    => 'no-reply@foobar.com',
50         subject => 'I am a Catalyst generated email',
51         # Specify which templates to include
52         templates => [
53             qw{text_plain/test.tt},
54             qw{text_html/test.tt}
55         ]
56     };
57     $c->forward('View::Email::Template');
58
59 Alternatively if you want more control over your templates you can use the following idiom :-
60
61     templates => [
62                 {       view => 'HTML', 
63                         template => 'email/test.html.tt',
64                         content_type => 'text/html'
65                 },
66                 {       view => 'Text', 
67                         template => 'email/test.plain.tt',
68                         content_type => 'text/plain'
69                 }
70
71     ]
72
73
74 If it fails $c->error will have the error message.
75
76 =cut
77
78 __PACKAGE__->config(
79     template_prefix => '',
80 );
81
82
83 # This view hitches into your default view and will call the render function
84 # on the templates provided.  This means that you have a layer of abstraction
85 # and you aren't required to modify your templates based on your desired engine
86 # (Template Toolkit or Mason, for example).  As long as the view adequately
87 # supports ->render, all things are good.  Mason, and others, are not good.
88
89 #
90 # The path here is to check configuration for the template root, and then
91 # proceed to call render on the subsequent templates and stuff each one
92 # into an Email::MIME container.  The mime-type will be stupidly guessed with
93 # the subdir on the template.
94 #
95 # TODO: Make this unretarded.
96 #
97 sub process {
98     my ( $self, $c ) = @_;
99
100     my $stash_key       = $self->config->{stash_key} || 'email';
101
102     croak "No template specified for rendering"
103         unless $c->stash->{$stash_key}->{template} or
104                 $c->stash->{$stash_key}->{templates};
105     # Where to look
106     my $template_prefix = $self->config->{template_prefix};
107     my @templates = ();
108
109     if ( $c->stash->{$stash_key}->{templates} && !ref $c->stash->{$stash_key}->{templates}[0]) {
110         push @templates, map {
111             join('/', $template_prefix, $_);
112         } @{$c->stash->{$stash_key}->{templates}};
113
114     } elsif($c->stash->{$stash_key}->{template}) {
115         push @templates, join('/', $template_prefix,
116             $c->stash->{$stash_key}->{template});
117     }
118    
119     my $default_view = $c->view( $self->config->{default_view} );
120
121     unless ( $default_view->can('render') ) {
122         croak "Email::Template's configured view does not have a render method!";
123     }
124
125     #$c->log->_dump($default_view->config);
126
127     my @parts = (); 
128     foreach my $template ( @templates ) {
129         $template =~ s#^/+##; # Make sure that we don't have an absolute path.
130         # This seems really stupid to me... argh.  will give me nightmares!
131         my $template_path = $template;
132             $template_path =~ s#^$template_prefix/##;
133         my ( $content_type, $extra ) = split('/', $template_path);
134         if ( $extra ) {
135             $content_type ||= 'text/plain';
136             $content_type =~  s#_#/#;
137         } else {
138             $content_type = 'text/plain';
139         }
140         my $output = $default_view->render( $c, $template,
141             { content_type => $content_type, %{$c->stash} });
142         # Got a ref, not a scalar.  An error!
143         if ( ref $output ) {
144             croak $output->can("as_string") ? $output->as_string : $output;
145         }
146         push @parts, Email::MIME->create(
147             attributes => {
148                 content_type => $content_type
149             },
150             body => $output
151         );
152     }
153
154         #add user parts :-
155         if ( $c->stash->{$stash_key}->{'templates'} && ref $c->stash->{$stash_key}->{templates}[0] ) {
156                 foreach my $part (@{$c->stash->{$stash_key}->{'templates'}}) {
157                         my $view = $c->view($part->{'view'} || $self->config->{default_view});
158
159                     my $content_type = $part->{'content_type'} || 'text/plain';
160                         unless ( $view->can('render') ) {
161                         croak "Part does not have valid render view";
162                     }
163
164                 my $output = $view->render( $c, $part->{'template'}, { 
165                                 'content_type' => $content_type, 
166                                 %{$c->stash} });
167                                 
168                 if ( ref $output ) {
169                     croak $output->can("as_string") ? $output->as_string : $output;
170                 }
171         
172                 push @parts, Email::MIME->create(
173                     attributes => {
174                         content_type => $content_type
175                     },
176                     body => $output
177                 );
178                 }
179         }
180
181     delete $c->stash->{email}->{body};
182     $c->stash->{email}->{parts} ||= [];
183     push @{$c->stash->{email}->{parts}}, @parts;
184
185     # Let C::V::Email do the actual sending.  We just assemble the tasty bits.
186     return $self->next::method($c);
187 }
188
189 =head1 TODO
190
191 =head2 ATTACHMENTS
192
193 There needs to be a method to support attachments.  What I am thinking is
194 something along these lines:
195
196     attachments => [
197         # Set the body to a file handle object, specify content_type and
198         # the file name. (name is what it is sent at, not the file)
199         { body => $fh, name => "foo.pdf", content_type => "application/pdf" },
200         # Or, specify a filename that is added, and hey, encoding!
201         { filename => "foo.gif", name => "foo.gif", content_type => "application/pdf", encoding => "quoted-printable" },
202         # Or, just a path to a file, and do some guesswork for the content type
203         "/path/to/somefile.pdf",
204     ]
205
206 =head1 SEE ALSO
207
208 =head2 L<Catalyst::View::Email> - Send plain boring emails with Catalyst
209
210 =head2 L<Catalyst::Manual> - The Catalyst Manual
211
212 =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
213
214 =head1 AUTHORS
215
216 J. Shirley <jshirley@gmail.com>
217
218 Simon Elliott <cpan@browsing.co.uk>
219
220 =head1 LICENSE
221
222 This library is free software, you can redistribute it and/or modify it under
223 the same terms as Perl itself.
224
225 =cut
226
227 1;
228