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