package CGI::FormBuilder::Template::Text;

=head1 NAME

CGI::FormBuilder::Template::Text - FormBuilder interface to Text::Template

=head1 SYNOPSIS

    my $form = CGI::FormBuilder->new(
                    fields   => \@fields,
                    template => {
                        type => 'Text',
                        template => 'form.tmpl',
                        variable => 'form',
                    }
               );

=cut

use Carp;
use strict;

our $VERSION = '3.03';

use CGI::FormBuilder::Util;
use Text::Template;

sub new {
    my $self  = shift;
    my $class = ref($self) || $self;
    my %opt   = @_;

    my $tt_engine = $opt{engine} || {};
    unless (UNIVERSAL::isa($tt_engine, 'Text::Template')) {
        $tt_engine->{&tt_param_name('type',%$tt_engine)}   ||= $opt{TYPE} || 'FILE';
        $tt_engine->{&tt_param_name('source',%$tt_engine)} ||= $opt{template} || $opt{source} ||
            puke "Text::Template source not specified, use the 'template' option";
        $tt_engine->{&tt_param_name('delimiters',%$tt_engine)} ||= [ '<%','%>' ];
        $opt{engine} = Text::Template->new(%$tt_engine) || puke $Text::Template::ERROR;
    }

    return bless \%opt, $class;
}

sub engine {
    return shift()->{engine};
}

# This sub helps us to support all of Text::Template's argument naming conventions
sub tt_param_name {
    my ($arg, %h) = @_;
    my ($key) = grep { exists $h{$_} } ($arg, "\u$arg", "\U$arg", "-$arg", "-\u$arg", "-\U$arg");
    return $key || $arg;
}

sub render {
    my $self = shift;
    my $form = shift;

    my %tmplvar = $form->tmpl_param;

    # Like Template Toolkit, Text::Template can directly access Perl data
    for my $field ($form->field) {

        # Extract value since used often
        my @value = $field->tag_value;

        # Create a struct for each field
        $tmplvar{field}{"$field"} = {
             %$field,
             field   => $field->tag,
             label   => $field->label,
             value   => $value[0],
             values  => \@value,
             options => [$field->options],
             type    => $field->type,
             comment => $field->comment,
        };
        $tmplvar{field}{"$field"}{error} = $field->error;
    }

    my $tt_data;
    if (ref $self->{data} eq 'ARRAY') {
        $tt_data = $self->{data};
    } else {
        $tt_data = [ $self->{data} ];
    }

    my $tt_var  = $self->{variable};      # optional var for nesting

    # must generate JS first because it affects the others
    $tmplvar{'jshead'} = $form->script;
    $tmplvar{'title'}  = $form->title;
    $tmplvar{'start'}  = $form->start . $form->statetags . $form->keepextras;
    $tmplvar{'submit'} = $form->submit;
    $tmplvar{'reset'}  = $form->reset;
    $tmplvar{'end'}    = $form->end;
    $tmplvar{'invalid'}= $form->invalid;
    $tmplvar{'fields'} = [ map $tmplvar{field}{$_}, $form->field ];
    if ($tt_var) {
        push @$tt_data, { $tt_var => \%tmplvar };
    } else {
        push @$tt_data, \%tmplvar;
    }

    my $tt_fill_in = $self->{fill_in} || {};
    my $tt_fill_in_hash = $tt_fill_in->{&tt_param_name('hash',%$tt_fill_in)} || {};
    if (ref($tt_fill_in_hash) eq 'ARRAY') {
        push @$tt_fill_in_hash, @$tt_data;
    } else {
        $tt_fill_in_hash = [ $tt_fill_in_hash, @$tt_data ];
    }

    $tt_fill_in_hash = {} unless @$tt_fill_in_hash;
    $tt_fill_in->{&tt_param_name('hash',%$tt_fill_in)} = $tt_fill_in_hash;
    my $tt_output = $self->{engine}->fill_in(%$tt_fill_in)
        || puke "Text::Template expansion failed: $Text::Template::ERROR";

    return $form->header . $tt_output;
}

1;
__END__

=head1 DESCRIPTION

This engine adapts B<FormBuilder> to use C<Text::Template>. Usage is very
similar to Template Toolkit:

    my $form = CGI::FormBuilder->new(
                    fields => \@fields,
                    template => {
                        type => 'Text',           # use Text::Template
                        template => 'form.tmpl',
                    }
               );

The default options passed into C<< Text::Template->new() >> with this
calling form are:

    TYPE   => 'FILE'
    SOURCE => 'form.tmpl'
    DELIMITERS => ['<%','%>']

As these params are passed for you, your template will look very similar to
ones used by Template Toolkit and C<HTML::Mason> (the Text::Template default
delimiters are C<{> and C<}>, but using alternative delimiters speeds it up by
about 25%, and the C<< <% >> and C<< %> >> delimiters are good,
familiar-looking alternatives).

    <% $jshead %>  -  JavaScript to stick in <head>
    <% $title  %>  -  The <title> of the HTML form
    <% $start  %>  -  Opening <form> tag and internal fields
    <% $submit %>  -  The submit button(s)
    <% $reset  %>  -  The reset button
    <% $end    %>  -  Closing </form> tag
    <% $fields %>  -  List of fields
    <% $field  %>  -  Hash of fields (for lookup by name)

Note that you refer to variables with a preceding C<$>, just like in Perl.
Like Template Toolkit, you can specify a variable to place fields under:

    my $form = CGI::FormBuilder->new(
        fields => \@fields,
        template => {
             type => 'Text',
             template => 'form.tmpl',
             variable => 'form'
        },
    );

Unlike Template Toolkit, though, these will not be placed in OO-style,
dot-separated vars. Instead, a hash will be created which you then reference:

    <% $form{jshead} %>
    <% $form{start}  %>
    etc.

And field data is in a hash-of-hashrefs format:

    For a field named...  The field data is in...
    --------------------  -----------------------
    job                   <% $form{field}{job}   %]
    size                  <% $form{field}{size}  %]
    email                 <% $form{field}{email} %]

Since C<Text::Template> looks so much like Perl, you can access individual
elements and create variables like so:

    <%
        my $myfield = $form{field}{email};
        $myfield->{label};      # text label
        $myfield->{field};      # field input tag
        $myfield->{value};      # first value
        $myfield->{values};     # list of all values
        $myfield->{options};    # list of all options
        $myfield->{required};   # required flag
        $myfield->{invalid};    # invalid flag
        $myfield->{error};      # error string if invalid
    %>

    <%
        for my $field (@{$form{fields}}) {
            $OUT .= "<tr>\n<td>" . $field->{label} . "</td> <td>" 
                                 . $field->{field} . "</td>\n<tr>";
        }
    %>

In addition, when using the engine option, you supply an existing
Text::Template object or a hash of parameters to be passed to C<new()>.
For example, you can ask for different delimiters yourself:

    my $form = CGI::FormBuilder->new(
        fields => \@fields,
        template => {
             type => 'Text',
             template => 'form.tmpl',
             variable => 'form',
             engine   => {
                DELIMITERS => [ '[@--', '--@]' ],
             },
             data => {
                  version => 1.23,
                  author  => 'Fred Smith',
             },
        },
    );

If you pass a hash of parameters, you can override the C<TYPE> and C<SOURCE> parameters,
as well as any other C<Text::Template> options. For example, you can pass in a string
template with C<< TYPE => STRING >> instead of loading it from a file. You must
specify B<both> C<TYPE> and C<SOURCE> if doing so.  The good news is this is trivial:

    my $form = CGI::FormBuilder->new(
        fields => \@fields,
        template => {
             type => 'Text',
             variable => 'form',
             engine   => {
                  TYPE => 'STRING',
                  SOURCE => $string,
                  DELIMITERS => [ '[@--', '--@]' ],
             },
             data => {
                  version => 1.23,
                  author  => 'Fred Smith',
             },
        },
    );

If you get the crazy idea to let users of your application pick the template file
(strongly discouraged) and you're getting errors, look at the C<Text::Template>
documentation for the C<UNTAINT> feature.

Also, note that C<Text::Template>'s C<< PREPEND => 'use strict;' >> option is not
recommended due to the dynamic nature for C<FormBuilder>.  If you use it, then you'll
have to declare each variable that C<FormBuilder> puts into your template with
C<< use vars qw($jshead' ... etc); >>

If you're really stuck on this, though, a workaround is to say:

    PREPEND => 'use strict; use vars qw(%form);'

and then set the option C<< variable => 'form' >>. That way you can have strict Perl
without too much hassle, except that your code might be exhausting to look at :-).
Things like C<$form{field}{your_field_name}{field}> end up being all over the place,
instead of the nicer short forms.

Finally, when you use the C<data> template option, the keys you specify will be available
to the template as regular variables. In the above example, these would be
C<< <% $version %> >> and C<< <% $author %> >>. And complex datatypes are easy:

    data => {
            anArray => [ 1, 2, 3 ],
            aHash => { orange => 'tangy', chocolate => 'sweet' },
    }

This becomes the following in your template:

    <%
        @anArray;    # you can use $myArray[1] etc.
        %aHash;      # you can use $myHash{chocolate} etc.
    %>

For more information, please consult the C<Text::Template> documentation.

=head1 SEE ALSO

L<CGI::FormBuilder>, L<CGI::FormBuilder::Template>, L<Text::Template>

=head1 REVISION

$Id: Text.pm,v 1.32 2006/02/24 01:42:29 nwiger Exp $

=head1 AUTHOR

Copyright (c) 2000-2006 Nathan Wiger <nate@wiger.org>. All Rights Reserved.

Text::Template support is due to huge contributions by Jonathan Buhacoff.
Thanks man.

This module is free software; you may copy this under the terms of
the GNU General Public License, or the Artistic License, copies of
which should have accompanied your Perl kit.

=cut