File Coverage

blib/lib/Catalyst/View/Email/Template.pm
Criterion Covered Total %
statement 9 36 25.0
branch 0 24 0.0
condition 0 15 0.0
subroutine 3 5 60.0
pod 1 1 100.0
total 13 81 16.0


line stmt bran cond sub pod time code
1             package Catalyst::View::Email::Template;
2              
3 1     1   4817 use Moose;
  1         2  
  1         6  
4 1     1   5825 use Carp;
  1         2  
  1         73  
5 1     1   5 use Scalar::Util qw/ blessed /;
  1         2  
  1         1061  
6             extends 'Catalyst::View::Email';
7              
8             our $VERSION = '0.36';
9             $VERSION = eval $VERSION;
10             =head1 NAME
11              
12             Catalyst::View::Email::Template - Send Templated Email from Catalyst
13              
14             =head1 SYNOPSIS
15              
16             Sends templated mail, based upon your default view. It captures the output
17             of the rendering path, slurps in based on mime-types and assembles a multi-part
18             email using L<Email::MIME::Creator> and sends it out.
19              
20             =head1 CONFIGURATION
21              
22             WARNING: since version 0.10 the configuration options slightly changed!
23              
24             Use the helper to create your view:
25            
26             $ script/myapp_create.pl view Email::Template Email::Template
27              
28             For basic configuration look at L<Catalyst::View::Email/CONFIGURATION>.
29              
30             In your app configuration (example in L<YAML>):
31              
32             View::Email::Template:
33             # Optional prefix to look somewhere under the existing configured
34             # template paths.
35             # Default: none
36             template_prefix: email
37             # Define the defaults for the mail
38             default:
39             # Defines the default view used to render the templates.
40             # If none is specified neither here nor in the stash
41             # Catalysts default view is used.
42             # Warning: if you don't tell Catalyst explicit which of your views should
43             # be its default one, C::V::Email::Template may choose the wrong one!
44             view: TT
45              
46             =head1 SENDING EMAIL
47              
48             Sending email works just like for L<Catalyst::View::Email> but by specifying
49             the template instead of the body and forwarding to your Email::Template view:
50              
51             sub controller : Private {
52             my ( $self, $c ) = @_;
53              
54             $c->stash->{email} = {
55             to => 'jshirley@gmail.com',
56             cc => 'abraxxa@cpan.org',
57             from => 'no-reply@foobar.com',
58             subject => 'I am a Catalyst generated email',
59             template => 'test.tt',
60             content_type => 'multipart/alternative'
61             };
62            
63             $c->forward( $c->view('Email::Template') );
64             }
65              
66             Alternatively if you want more control over your templates you can use the following idiom
67             to override the defaults. If charset and encoding given, the body become properly encoded.
68              
69             templates => [
70             {
71             template => 'email/test.html.tt',
72             content_type => 'text/html',
73             charset => 'utf-8',
74             encoding => 'quoted-printable',
75             view => 'TT',
76             },
77             {
78             template => 'email/test.plain.mason',
79             content_type => 'text/plain',
80             charset => 'utf-8',
81             encoding => 'quoted-printable',
82             view => 'Mason',
83             }
84             ]
85              
86              
87              
88             =head1 HANDLING ERRORS
89              
90             See L<Catalyst::View::Email/HANDLING ERRORS>.
91              
92             =cut
93              
94             # here the defaults of Catalyst::View::Email are extended by the additional
95             # ones Template.pm needs.
96              
97             has 'stash_key' => (
98             is => 'rw',
99             isa => 'Str',
100             default => sub { "email" },
101             lazy => 1,
102             );
103              
104             has 'template_prefix' => (
105             is => 'rw',
106             isa => 'Str',
107             default => sub { '' },
108             lazy => 1,
109             );
110              
111             has 'default' => (
112             is => 'rw',
113             isa => 'HashRef',
114             default => sub {
115             {
116             view => 'TT',
117             content_type => 'text/html',
118             };
119             },
120             lazy => 1,
121             );
122              
123             # This view hitches into your default view and will call the render function
124             # on the templates provided. This means that you have a layer of abstraction
125             # and you aren't required to modify your templates based on your desired engine
126             # (Template Toolkit or Mason, for example). As long as the view adequately
127             # supports ->render, all things are good. Mason, and others, are not good.
128              
129             #
130             # The path here is to check configuration for the template root, and then
131             # proceed to call render on the subsequent templates and stuff each one
132             # into an Email::MIME container. The mime-type will be stupidly guessed with
133             # the subdir on the template.
134             #
135              
136             # Set it up so if you have multiple parts, they're alternatives.
137             # This is on the top-level message, not the individual parts.
138             #multipart/alternative
139              
140             sub _validate_view {
141 0     0     my ( $self, $view ) = @_;
142              
143 0 0         croak "C::V::Email::Template's configured view '$view' isn't an object!"
144             unless ( blessed($view) );
145              
146 0 0         croak
147             "C::V::Email::Template's configured view '$view' isn't an Catalyst::View!"
148             unless ( $view->isa('Catalyst::View') );
149              
150 0 0         croak
151             "C::V::Email::Template's configured view '$view' doesn't have a render method!"
152             unless ( $view->can('render') );
153             }
154              
155             =head1 METHODS
156              
157             =over 4
158              
159             =item generate_part
160              
161             Generates a MIME part to include in the email. Since the email is template based
162             every template piece is a separate part that is included in the email.
163              
164             =cut
165              
166             sub generate_part {
167 0     0 1   my ( $self, $c, $attrs ) = @_;
168              
169 0           my $template_prefix = $self->template_prefix;
170 0           my $default_view = $self->default->{view};
171 0           my $default_content_type = $self->default->{content_type};
172 0           my $default_charset = $self->default->{charset};
173              
174 0           my $view;
175              
176             # use the view specified for the email part
177 0 0 0       if ( exists $attrs->{view}
    0 0        
178             && defined $attrs->{view}
179             && $attrs->{view} ne '' )
180             {
181 0           $view = $c->view( $attrs->{view} );
182 0 0         $c->log->debug(
183             "C::V::Email::Template uses specified view $view for rendering.")
184             if $c->debug;
185             }
186              
187             # if none specified use the configured default view
188             elsif ($default_view) {
189 0           $view = $c->view($default_view);
190 0 0         $c->log->debug(
191             "C::V::Email::Template uses default view $view for rendering.")
192             if $c->debug;
193             }
194              
195             # else fallback to Catalysts default view
196             else {
197 0           $view = $c->view;
198 0 0         $c->log->debug(
199             "C::V::Email::Template uses Catalysts default view $view for rendering."
200             ) if $c->debug;
201             }
202              
203             # validate the per template view
204 0           $self->_validate_view($view);
205              
206             # prefix with template_prefix if configured
207             my $template =
208             $template_prefix ne ''
209             ? join( '/', $template_prefix, $attrs->{template} )
210 0 0         : $attrs->{template};
211              
212             # setup the attributes (merge with defaults)
213 0           my $e_m_attrs = $self->SUPER::setup_attributes( $c, $attrs );
214              
215             # render the email part
216             my $output = $view->render(
217             $c,
218             $template,
219             {
220             content_type => $e_m_attrs->{content_type},
221             stash_key => $self->stash_key,
222 0           %{$c->stash},
  0            
223             }
224             );
225 0 0         if ( ref $output ) {
226 0 0         croak $output->can('as_string') ? $output->as_string : $output;
227             }
228              
229 0 0 0       if ( exists $e_m_attrs->{encoding}
      0        
      0        
230             && defined $e_m_attrs->{encoding}
231             && exists $e_m_attrs->{charset}
232             && defined $e_m_attrs->{charset} ) {
233              
234 0           return Email::MIME->create(
235             attributes => $e_m_attrs,
236             body_str => $output,
237             );
238              
239             } else {
240              
241 0           return Email::MIME->create(
242             attributes => $e_m_attrs,
243             body => $output,
244             );
245             }
246             }
247              
248             =item process
249              
250             The process method is called when the view is dispatched to. This creates the
251             multipart message and then sends the message contents off to
252             L<Catalyst::View::Email> for processing, which in turn hands off to
253             L<Email::Sender::Simple>.
254              
255             =cut
256              
257             around 'process' => sub {
258             my ( $orig, $self, $c, @args ) = @_;
259             my $stash_key = $self->stash_key;
260             return $self->$orig( $c, @args )
261             unless $c->stash->{$stash_key}->{template}
262             or $c->stash->{$stash_key}->{templates};
263              
264             # in case of the simple api only one
265             my @parts = ();
266              
267             # now find out if the single or multipart api was used
268             # prefer the multipart one
269              
270             # multipart api
271             if ( $c->stash->{$stash_key}->{templates}
272             && ref $c->stash->{$stash_key}->{templates} eq 'ARRAY'
273             && ref $c->stash->{$stash_key}->{templates}[0] eq 'HASH' )
274             {
275              
276             # loop through all parts of the mail
277             foreach my $part ( @{ $c->stash->{$stash_key}->{templates} } ) {
278             push @parts,
279             $self->generate_part(
280             $c,
281             {
282             view => $part->{view},
283             template => $part->{template},
284             content_type => $part->{content_type},
285             charset => $part->{charset},
286             encoding => $part->{encoding},
287             }
288             );
289             }
290             }
291              
292             # single part api
293             elsif ( $c->stash->{$stash_key}->{template} ) {
294             my $part_args = { template => $c->stash->{$stash_key}->{template} };
295             if (my $ctype = $c->stash->{$stash_key}->{content_type}) {
296             $part_args->{content_type} = $ctype;
297             }
298             push @parts,
299             $self->generate_part( $c, $part_args );
300             }
301              
302             delete $c->stash->{$stash_key}->{body};
303             $c->stash->{$stash_key}->{parts} ||= [];
304             push @{ $c->stash->{$stash_key}->{parts} }, @parts;
305            
306             return $self->$orig($c);
307              
308             };
309              
310             =back
311              
312             =head1 TODO
313              
314             =head2 ATTACHMENTS
315              
316             There needs to be a method to support attachments. What I am thinking is
317             something along these lines:
318              
319             attachments => [
320             # Set the body to a file handle object, specify content_type and
321             # the file name. (name is what it is sent at, not the file)
322             { body => $fh, name => "foo.pdf", content_type => "application/pdf" },
323             # Or, specify a filename that is added, and hey, encoding!
324             { filename => "foo.gif", name => "foo.gif", content_type => "application/pdf", encoding => "quoted-printable" },
325             # Or, just a path to a file, and do some guesswork for the content type
326             "/path/to/somefile.pdf",
327             ]
328              
329             =head1 SEE ALSO
330              
331             =head2 L<Catalyst::View::Email> - Send plain boring emails with Catalyst
332              
333             =head2 L<Catalyst::Manual> - The Catalyst Manual
334              
335             =head2 L<Catalyst::Manual::Cookbook> - The Catalyst Cookbook
336              
337             =head1 AUTHORS
338              
339             J. Shirley <jshirley@gmail.com>
340              
341             Simon Elliott <cpan@browsing.co.uk>
342              
343             Alexander Hartmaier <abraxxa@cpan.org>
344              
345             =head1 LICENSE
346              
347             This library is free software, you can redistribute it and/or modify it under
348             the same terms as Perl itself.
349              
350             =cut
351              
352             1;