source: text-formbuilder/trunk/lib/Text/FormBuilder.pm @ 81

Last change on this file since 81 was 81, checked in by peichman, 19 years ago

added a !reset directive to name and include a reset button

File size: 39.9 KB
RevLine 
[1]1package Text::FormBuilder;
2
3use strict;
4use warnings;
5
[53]6use base qw(Exporter Class::ParseText::Base);
[42]7use vars qw($VERSION @EXPORT);
[1]8
[80]9$VERSION = '0.11_01';
[42]10@EXPORT = qw(create_form);
[19]11
[63]12#$::RD_TRACE = 1;
13
[16]14use Carp;
[1]15use Text::FormBuilder::Parser;
16use CGI::FormBuilder;
17
[69]18use Data::Dumper;
19$Data::Dumper::Terse = 1;           # don't dump $VARn names
20$Data::Dumper::Quotekeys = 0;       # don't quote simple string keys
21
[28]22# the static default options passed to CGI::FormBuilder->new
[23]23my %DEFAULT_OPTIONS = (
24    method => 'GET',
25    keepextras => 1,
26);
27
[30]28# the built in CSS for the template
29my $DEFAULT_CSS = <<END;
30table { padding: 1em; }
[60]31td table { padding: 0; } /* exclude the inner checkbox tables */
[30]32#author, #footer { font-style: italic; }
33caption h2 { padding: .125em .5em; background: #ccc; text-align: left; }
[71]34fieldset { margin: 1em 0; border: none; border-top: 2px solid #999; }
[67]35legend { font-size: 1.25em; font-weight: bold; background: #ccc; padding: .125em .25em; border: 1px solid #666; }
[30]36th { text-align: left; }
[71]37th h2 { padding: .125em .5em; background: #eee; font-size: 1.25em; }
[30]38th.label { font-weight: normal; text-align: right; vertical-align: top; }
39td ul { list-style: none; padding-left: 0; margin-left: 0; }
[71]40.note { background: #eee; padding: .5em 1em; }
[30]41.sublabel { color: #999; }
42.invalid { background: red; }
43END
44
[34]45# default messages that can be localized
[33]46my %DEFAULT_MESSAGES = (
[34]47    text_author   => 'Created by %s',
48    text_madewith => 'Made with %s version %s',
49    text_required => '(Required fields are marked in <strong>bold</strong>.)',
50    text_invalid  => 'Missing or invalid value.',
[33]51);
52
[34]53my $DEFAULT_CHARSET = 'iso-8859-1';
54
[39]55# options to clean up the code with Perl::Tidy
56my $TIDY_OPTIONS = '-nolq -ci=4 -ce';
57
[42]58my $HTML_EXTS   = qr/\.html?$/;
[46]59my $MODULE_EXTS = qr/\.pm$/;
[42]60my $SCRIPT_EXTS = qr/\.(pl|cgi)$/;
61
62# superautomagical exported function
63sub create_form {
64    my ($source, $options, $destination) = @_;
65    my $parser = __PACKAGE__->parse($source);
66    $parser->build(%{ $options || {} });
67    if ($destination) {
68        if (ref $destination) {
[53]69            croak '[' . (caller(0))[3] . "] Don't know what to do with a ref for $destination";
70            #TODO: what DO ref dests mean?
[42]71        } else {
72            # write webpage, script, or module
[46]73            if ($destination =~ $MODULE_EXTS) {
[52]74                $parser->write_module($destination, 1);
[42]75            } elsif ($destination =~ $SCRIPT_EXTS) {
[52]76                $parser->write_script($destination, 1);
[42]77            } else {
[46]78                $parser->write($destination);
[42]79            }
80        }
81    } else {
82        defined wantarray ? return $parser->form : $parser->write;
83    }
84}
85
[53]86# subclass of Class::ParseText::Base
87sub init {
88    my $self = shift;
89    $self->{parser}         = Text::FormBuilder::Parser->new;
90    $self->{start_rule}     = 'form_spec';
91    $self->{ensure_newline} = 1;
[42]92    return $self;
93}
94
[33]95# this is where a lot of the magic happens
[1]96sub build {
97    my ($self, %options) = @_;
[12]98   
99    # our custom %options:
100    # form_only: use only the form part of the template
101    my $form_only = $options{form_only};
[33]102   
103    # css, extra_css: allow for custom inline stylesheets
[38]104    #   neat trick: css => '@import(my_external_stylesheet.css);'
[33]105    #   will let you use an external stylesheet
[34]106    #   CSS Hint: to get multiple sections to all line up their fields,
107    #   set a standard width for th.label
[72]108    # external_css: scalar for a single external stylesheet; array for
109    #   multiple sheets; prepended to the beginning of the CSS as @import
110    #   statetments
[30]111    my $css;
112    $css = $options{css} || $DEFAULT_CSS;
[72]113    if ($options{external_css}) {
114        my $ref = ref $options{external_css};
115        if ($ref eq 'ARRAY') {
116            # loop over the list of external sheets
117            my $external_sheets = join("\n", map { "\@import url($_);" } @{ $options{external_css} });
118            $css = "$external_sheets\n$css";
119        } elsif ($ref) {
120            croak '[' . (caller(0))[3] . "] Don't know how to handle $ref reference as an argument to external_css";
121        } else {
122            $css = "\@import url($options{external_css});\n$css";
123        }
124    }
[30]125    $css .= $options{extra_css} if $options{extra_css};
[12]126   
[33]127    # messages
[38]128    # code pulled (with modifications) from CGI::FormBuilder
[33]129    if ($options{messages}) {
130        # if its a hashref, we'll just pass it on to CGI::FormBuilder
131       
132        if (my $ref = ref $options{messages}) {
133            # hashref pass on to CGI::FormBuilder
134            croak "[Text::FormBuilder] Argument to 'messages' option must be a filename or hashref" unless $ref eq 'HASH';
135            while (my ($key,$value) = each %DEFAULT_MESSAGES) {
136                $options{messages}{$key} ||= $DEFAULT_MESSAGES{$key};
137            }
138        } else {
139            # filename, just *warn* on missing, and use defaults
140            if (-f $options{messages} && -r _ && open(MESSAGES, "< $options{messages}")) {
[34]141                $options{messages} = { %DEFAULT_MESSAGES };
[33]142                while(<MESSAGES>) {
143                    next if /^\s*#/ || /^\s*$/;
144                    chomp;
145                    my($key,$value) = split ' ', $_, 2;
146                    ($options{messages}{$key} = $value) =~ s/\s+$//;
147                }
148                close MESSAGES;
149            } else {
[53]150                carp '[' . (caller(0))[3] . "] Could not read messages file $options{messages}: $!";
[33]151            }
[1]152        }
[34]153    } else {
154        $options{messages} = { %DEFAULT_MESSAGES };
[1]155    }
156   
[53]157    # character set
[34]158    my $charset = $options{charset};
159   
[33]160    # save the build options so they can be used from write_module
161    $self->{build_options} = { %options };
162   
163    # remove our custom options before we hand off to CGI::FormBuilder
[34]164    delete $options{$_} foreach qw(form_only css extra_css charset);
[33]165   
[21]166    # expand groups
[70]167    if (my %groups = %{ $self->{form_spec}{groups} || {} }) {       
[42]168        for my $section (@{ $self->{form_spec}{sections} || [] }) {
169            foreach (grep { $$_[0] eq 'group' } @{ $$section{lines} }) {
170                $$_[1]{group} =~ s/^\%//;       # strip leading % from group var name
171               
172                if (exists $groups{$$_[1]{group}}) {
173                    my @fields; # fields in the group
174                    push @fields, { %$_ } foreach @{ $groups{$$_[1]{group}} };
175                    for my $field (@fields) {
176                        $$field{label} ||= ucfirst $$field{name};
177                        $$field{name} = "$$_[1]{name}_$$field{name}";               
178                    }
[73]179                    $_ = [
180                        'group',
181                        {
182                            label => $$_[1]{label} || ucfirst(join(' ',split('_',$$_[1]{name}))),
183                            comment => $$_[1]{comment},
184                            group => \@fields,
185                        },
186                    ];
[29]187                }
[21]188            }
189        }
190    }
[1]191   
[33]192    # the actual fields that are given to CGI::FormBuilder
[42]193    # make copies so that when we trim down the sections
194    # we don't lose the form field information
[21]195    $self->{form_spec}{fields} = [];
[29]196   
197    for my $section (@{ $self->{form_spec}{sections} || [] }) {
198        for my $line (@{ $$section{lines} }) {
199            if ($$line[0] eq 'group') {
[42]200                push @{ $self->{form_spec}{fields} }, { %{$_} } foreach @{ $$line[1]{group} };
[29]201            } elsif ($$line[0] eq 'field') {
[42]202                push @{ $self->{form_spec}{fields} }, { %{$$line[1]} };
[29]203            }
[21]204        }
205    }
206   
[39]207    # substitute in custom validation subs and pattern definitions for field validation
208    my %patterns = %{ $self->{form_spec}{patterns} || {} };
209    my %subs = %{ $self->{form_spec}{subs} || {} };
210   
211    foreach (@{ $self->{form_spec}{fields} }) {
212        if ($$_{validate}) {
213            if (exists $patterns{$$_{validate}}) {
[33]214                $$_{validate} = $patterns{$$_{validate}};
[39]215            # TODO: need the Data::Dumper code to work for this
216            # for now, we just warn that it doesn't work
217            } elsif (exists $subs{$$_{validate}}) {
[53]218                warn '[' . (caller(0))[3] . "] validate coderefs don't work yet";
[39]219                delete $$_{validate};
[42]220##                 $$_{validate} = $subs{$$_{validate}};
[33]221            }
222        }
223    }
224   
[50]225    # get user-defined lists; can't make this conditional because
226    # we need to be able to fall back to CGI::FormBuilder's lists
227    # even if the user didn't define any
228    my %lists = %{ $self->{form_spec}{lists} || {} };
229   
[1]230    # substitute in list names
[50]231    foreach (@{ $self->{form_spec}{fields} }) {
232        next unless $$_{list};
233       
234        $$_{list} =~ s/^\@//;   # strip leading @ from list var name
235       
236        # a hack so we don't get screwy reference errors
237        if (exists $lists{$$_{list}}) {
238            my @list;
239            push @list, { %$_ } foreach @{ $lists{$$_{list}} };
240            $$_{options} = \@list;
241        } else {
242            # assume that the list name is a builtin
243            # and let it fall through to CGI::FormBuilder
244            $$_{options} = $$_{list};
[1]245        }
[50]246    } continue {
247        delete $$_{list};
[30]248    }
[21]249   
[30]250    # special case single-value checkboxes
251    foreach (grep { $$_{type} && $$_{type} eq 'checkbox' } @{ $self->{form_spec}{fields} }) {
252        unless ($$_{options}) {
253            $$_{options} = [ { $$_{name} => $$_{label} || ucfirst join(' ',split(/_/,$$_{name})) } ];
254        }
255    }
256   
[63]257    # use columns for displaying checkbox fields larger than 2 items
[14]258    foreach (@{ $self->{form_spec}{fields} }) {
[60]259        if (ref $$_{options} and @{ $$_{options} } >= 3) {
260            $$_{columns} = int(@{ $$_{options} } / 8) + 1;
261        }
[14]262    }
[1]263   
[24]264    # remove extraneous undefined values
[64]265    # also check for approriate version of CGI::FormBuilder
266    # for some advanced options
267    my $FB_version = CGI::FormBuilder->VERSION;
[24]268    for my $field (@{ $self->{form_spec}{fields} }) {
269        defined $$field{$_} or delete $$field{$_} foreach keys %{ $field };
[64]270       
[66]271        unless ($FB_version >= '3.02') {
272            for (qw(growable other)) {
273                if ($$field{$_}) {
274                    warn '[' . (caller(0))[3] . "] '$_' fields not supported by FB $FB_version (requires 3.02)";
275                    delete $$field{$_};
276                }
[64]277            }
278        }
[68]279    }
[24]280   
[64]281    # assign the field names to the sections
[39]282    foreach (@{ $self->{form_spec}{sections} }) {
[42]283        for my $line (@{ $$_{lines} }) {
284            if ($$line[0] eq 'field') {
285                $$line[1] = $$line[1]{name};
286            }
[39]287        }
288    }
289   
[69]290    # gather together all of the form options
291    $self->{form_options} = {
[23]292        %DEFAULT_OPTIONS,
[33]293        # need to explicity set the fields so that simple text fields get picked up
[29]294        fields   => [ map { $$_{name} } @{ $self->{form_spec}{fields} } ],
[24]295        required => [ map { $$_{name} } grep { $$_{required} } @{ $self->{form_spec}{fields} } ],
296        title => $self->{form_spec}{title},
[25]297        text  => $self->{form_spec}{description},
[81]298        # use 'defined' so we are able to differentiate between 'submit = 0' (no submit button)
299        # and 'submit = undef' (use default submit button)
300        ( defined $self->{form_spec}{submit} ? (submit => $self->{form_spec}{submit}) : () ),
301        reset => $self->{form_spec}{reset},
[1]302        template => {
303            type => 'Text',
304            engine => {
305                TYPE       => 'STRING',
[34]306                SOURCE     => $form_only ? $self->_form_template : $self->_template($css, $charset),
[11]307                DELIMITERS => [ qw(<% %>) ],
[1]308            },
309            data => {
[81]310                #TODO: make FB aware of sections
[29]311                sections    => $self->{form_spec}{sections},
[14]312                author      => $self->{form_spec}{author},
313                description => $self->{form_spec}{description},
[1]314            },
315        },
316        %options,
[69]317    };
318   
319    # create the form object
320    $self->{form} = CGI::FormBuilder->new(%{ $self->{form_options} });
321   
322    # ...and set up its fields
[1]323    $self->{form}->field(%{ $_ }) foreach @{ $self->{form_spec}{fields} };
324   
[16]325    # mark structures as built
326    $self->{built} = 1;
327   
[1]328    return $self;
329}
330
331sub write {
332    my ($self, $outfile) = @_;
[16]333   
334    # automatically call build if needed to
335    # allow the new->parse->write shortcut
336    $self->build unless $self->{built};
337   
[1]338    if ($outfile) {
339        open FORM, "> $outfile";
340        print FORM $self->form->render;
341        close FORM;
342    } else {
343        print $self->form->render;
344    }
345}
346
[69]347# dump the form options as eval-able code
348sub _form_options_code {
[39]349    my $self = shift;
[69]350    my $d = Data::Dumper->new([ $self->{form_options} ], [ '*options' ]);
351    return keys %{ $self->{form_options} } > 0 ? $d->Dump : '';   
352}
353# dump the field setup subs as eval-able code
354# pass in the variable name of the form object
355# (defaults to '$form')
[70]356# TODO: revise this code to use the new 'fieldopts'
357# option to the FB constructor (requires FB 3.02)
[69]358sub _field_setup_code {
359    my $self = shift;
360    my $object_name = shift || '$form';
361    return join(
[12]362        "\n", 
[69]363        map { $object_name . '->field' . Data::Dumper->Dump([$_],['*field']) . ';' } @{ $self->{form_spec}{fields} }
[12]364    );
[69]365}   
[39]366
367sub write_module {
368    my ($self, $package, $use_tidy) = @_;
369
[53]370    croak '[' . (caller(0))[3] . '] Expecting a package name' unless $package;
[39]371   
[52]372    # remove a trailing .pm
373    $package =~ s/\.pm$//;
374##     warn  "[Text::FromBuilder::write_module] Removed extra '.pm' from package name\n" if $package =~ s/\.pm$//;
375   
[69]376    my $form_options = $self->_form_options_code;
377    my $field_setup = $self->_field_setup_code('$self');
[39]378   
[69]379    # old style of module
380    # TODO: how to keep this (as deprecated method)
381    my $old_module = <<END;
[12]382package $package;
383use strict;
384use warnings;
385
386use CGI::FormBuilder;
387
[15]388sub get_form {
[39]389    my \$q = shift;
390
[69]391    my \$self = CGI::FormBuilder->new(
392        $form_options,
393        \@_,
394    );
[12]395   
[69]396    $field_setup
397   
398    return \$self;
[12]399}
400
401# module return
4021;
403END
[23]404
[69]405    # new style of module
406    my $module = <<END;
407package $package;
408use strict;
409use warnings;
410
411use base qw(CGI::FormBuilder);
412
413sub new {
414    my \$invocant = shift;
415    my \$class = ref \$invocant || \$invocant;
416   
417    my \$self = CGI::FormBuilder->new(
418        $form_options,
419        \@_,
420    );
421   
422    $field_setup
423   
424    # re-bless into this class
425    bless \$self, \$class;
426}
427
428# module return
4291;
430END
[42]431    _write_output_file($module, (split(/::/, $package))[-1] . '.pm', $use_tidy);
[39]432    return $self;
433}
434
435sub write_script {
436    my ($self, $script_name, $use_tidy) = @_;
437
[53]438    croak '[' . (caller(0))[3] . '] Expecting a script name' unless $script_name;
[39]439   
[69]440    my $form_options = $self->_form_options_code;
441    my $field_setup = $self->_field_setup_code('$form');
442
[39]443    my $script = <<END;
444#!/usr/bin/perl
445use strict;
446use warnings;
447
448use CGI::FormBuilder;
449
[69]450my \$form = CGI::FormBuilder->new(
451    $form_options
452);
[39]453
[69]454$field_setup
[39]455   
456unless (\$form->submitted && \$form->validate) {
457    print \$form->render;
458} else {
459    # do something with the entered data
460}
461END
462   
463    _write_output_file($script, $script_name, $use_tidy);   
464    return $self;
465}
466
467sub _write_output_file {
468    my ($source_code, $outfile, $use_tidy) = @_;
[16]469    if ($use_tidy) {
470        # clean up the generated code, if asked
471        eval 'use Perl::Tidy';
[52]472        unless ($@) {
473            Perl::Tidy::perltidy(source => \$source_code, destination => $outfile, argv => $TIDY_OPTIONS);
474        } else {
[53]475            carp '[' . (caller(0))[3] . "] Can't tidy the code: $@" if $@;
[52]476            # fallback to just writing it as-is
477            open OUT, "> $outfile" or die $!;
478            print OUT $source_code;
479            close OUT;
480        }
[16]481    } else {
482        # otherwise, just print as is
[39]483        open OUT, "> $outfile" or die $!;
484        print OUT $source_code;
485        close OUT;
[12]486    }
487}
488
[39]489
[16]490sub form {
491    my $self = shift;
492   
493    # automatically call build if needed to
494    # allow the new->parse->write shortcut
495    $self->build unless $self->{built};
[1]496
[16]497    return $self->{form};
498}
499
[12]500sub _form_template {
[33]501    my $self = shift;
[34]502    my $msg_required = $self->{build_options}{messages}{text_required};
503    my $msg_invalid = $self->{build_options}{messages}{text_invalid};
504    return q{<% $description ? qq[<p id="description">$description</p>] : '' %>
505<% (grep { $_->{required} } @fields) ? qq[<p id="instructions">} . $msg_required . q{</p>] : '' %>
[12]506<% $start %>
[23]507<%
508    # drop in the hidden fields here
509    $OUT = join("\n", map { $$_{field} } grep { $$_{type} eq 'hidden' } @fields);
[34]510%>} .
511q[
[29]512<%
513    SECTION: while (my $section = shift @sections) {
[67]514        $OUT .= qq[<fieldset>\n];
515        $OUT .= qq[  <legend>$$section{head}</legend>] if $$section{head};
[30]516        $OUT .= qq[<table id="] . ($$section{id} || '_default') . qq[">\n];
[67]517        #$OUT .= qq[  <caption><h2 class="sectionhead">$$section{head}</h2></caption>] if $$section{head};
[29]518        TABLE_LINE: for my $line (@{ $$section{lines} }) {
519            if ($$line[0] eq 'head') {
[71]520                $OUT .= qq[  <tr><th class="subhead" colspan="2"><h2>$$line[1]</h2></th></tr>\n]
[50]521            } elsif ($$line[0] eq 'note') {
522                $OUT .= qq[  <tr><td class="note" colspan="2">$$line[1]</td></tr>\n]
[29]523            } elsif ($$line[0] eq 'field') {
[42]524                local $_ = $field{$$line[1]};
[34]525               
[29]526                # skip hidden fields in the table
527                next TABLE_LINE if $$_{type} eq 'hidden';
528               
529                $OUT .= $$_{invalid} ? qq[  <tr class="invalid">] : qq[  <tr>];
[30]530               
531                # special case single value checkboxes
532                if ($$_{type} eq 'checkbox' && @{ $$_{options} } == 1) {
533                    $OUT .= qq[<th></th>];
534                } else {
[66]535                    $OUT .= '<th class="label">' . ($$_{required} ? qq[<strong class="required">$$_{label}</strong>] : "$$_{label}") . '</th>';
[30]536                }
[33]537               
538                # mark invalid fields
[29]539                if ($$_{invalid}) {
[81]540                    $OUT .= qq[<td>$$_{field} <span class="comment">$$_{comment}</span> $$_{error}</td>];
[29]541                } else {
[73]542                    $OUT .= qq[<td>$$_{field} <span class="comment">$$_{comment}</span></td>];
[29]543                }
[33]544               
[30]545                $OUT .= qq[</tr>\n];
546               
[29]547            } elsif ($$line[0] eq 'group') {
[42]548                my @group_fields = map { $field{$_} } map { $$_{name} } @{ $$line[1]{group} };
[29]549                $OUT .= (grep { $$_{invalid} } @group_fields) ? qq[  <tr class="invalid">\n] : qq[  <tr>\n];
550               
551                $OUT .= '    <th class="label">';
[70]552                $OUT .= (grep { $$_{required} } @group_fields) ? qq[<strong class="required">$$line[1]{label}</strong>] : "$$line[1]{label}";
[29]553                $OUT .= qq[</th>\n];
554               
[59]555                $OUT .= qq[    <td><span class="fieldgroup">];
[29]556                $OUT .= join(' ', map { qq[<small class="sublabel">$$_{label}</small> $$_{field} $$_{comment}] } @group_fields);
[81]557                if (my @invalid = grep { $$_{invalid} } @group_fields) {
558                    $OUT .= ' ' . join('; ', map { $$_{error} } @invalid);
559                }               
[73]560                $OUT .= qq[ <span class="comment">$$line[1]{comment}</span></span></td>\n];
[29]561                $OUT .= qq[  </tr>\n];
562            }   
[21]563        }
[29]564        # close the table if there are sections remaining
565        # but leave the last one open for the submit button
[67]566        if (@sections) {
567            $OUT .= qq[</table>\n];
568            $OUT .= qq[</fieldset>\n];
569        }
[29]570    }
571%>
[81]572  <tr><th></th><td style="padding-top: 1em;"><% $submit %> <% $reset %></td></tr>
[12]573</table>
[67]574</fieldset>
[12]575<% $end %>
576];
577}
578
[34]579# usage: $self->_pre_template($css, $charset)
[33]580sub _pre_template {
[12]581    my $self = shift;
[30]582    my $css = shift || $DEFAULT_CSS;
[34]583    my $charset = shift || $DEFAULT_CHARSET;
584    my $msg_author = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_author}) . '", $author)';
[33]585    return 
[12]586q[<html>
[1]587<head>
[34]588  <meta http-equiv="Content-Type" content="text/html; charset=] . $charset . q[" />
[1]589  <title><% $title %><% $author ? ' - ' . ucfirst $author : '' %></title>
[33]590  <style type="text/css">
[42]591] . $css . q[  </style>
[33]592  <% $jshead %>
[1]593</head>
594<body>
595
596<h1><% $title %></h1>
[34]597<% $author ? qq[<p id="author">] . ] . $msg_author . q{ . q[</p>] : '' %>
598};
[33]599}
600
601sub _post_template {
[34]602    my $self = shift;
603    my $msg_madewith = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_madewith}) .
604        '", q[<a href="http://formbuilder.org/">CGI::FormBuilder</a>], CGI::FormBuilder->VERSION)';
605   
606    return qq[<hr />
[1]607<div id="footer">
[34]608  <p id="creator"><% $msg_madewith %></p>
[1]609</div>
610</body>
611</html>
612];
613}
614
[42]615# usage: $self->_template($css, $charset)
[33]616sub _template {
617    my $self = shift;
[34]618    return $self->_pre_template(@_) . $self->_form_template . $self->_post_template;
[33]619}
620
[7]621sub dump { 
622    eval "use YAML;";
623    unless ($@) {
624        print YAML::Dump(shift->{form_spec});
625    } else {
[53]626        warn '[' . (caller(0))[3] . "] Can't dump form spec structure using YAML: $@";
[7]627    }
628}
[1]629
630
631# module return
6321;
633
634=head1 NAME
635
[21]636Text::FormBuilder - Create CGI::FormBuilder objects from simple text descriptions
[1]637
638=head1 SYNOPSIS
639
[16]640    use Text::FormBuilder;
641   
[1]642    my $parser = Text::FormBuilder->new;
643    $parser->parse($src_file);
644   
[16]645    # returns a new CGI::FormBuilder object with
646    # the fields from the input form spec
[7]647    my $form = $parser->form;
[19]648   
649    # write a My::Form module to Form.pm
650    $parser->write_module('My::Form');
[1]651
[23]652=head1 REQUIRES
653
[56]654L<Parse::RecDescent>,
655L<CGI::FormBuilder>,
656L<Text::Template>,
657L<Class::Base>
[23]658
[1]659=head1 DESCRIPTION
660
[38]661This module is intended to extend the idea of making it easy to create
662web forms by allowing you to describe them with a simple langauge. These
663I<formspecs> are then passed through this module's parser and converted
664into L<CGI::FormBuilder> objects that you can easily use in your CGI
665scripts. In addition, this module can generate code for standalone modules
666which allow you to separate your form design from your script code.
667
668A simple formspec looks like this:
669
670    name//VALUE
671    email//EMAIL
672    langauge:select{English,Spanish,French,German}
673    moreinfo|Send me more information:checkbox
674    interests:checkbox{Perl,karate,bass guitar}
675
676This will produce a required C<name> test field, a required C<email> text
677field that must look like an email address, an optional select dropdown
678field C<langauge> with the choices English, Spanish, French, and German,
679an optional C<moreinfo> checkbox labeled ``Send me more information'', and
680finally a set of checkboxes named C<interests> with the choices Perl,
681karate, and bass guitar.
682
[46]683=head1 METHODS
684
[1]685=head2 new
686
[38]687    my $parser = Text::FormBuilder->new;
688
[1]689=head2 parse
690
[42]691    # parse a file (regular scalar)
[19]692    $parser->parse($filename);
[7]693   
[19]694    # or pass a scalar ref for parse a literal string
695    $parser->parse(\$string);
[42]696   
697    # or an array ref to parse lines
698    $parser->parse(\@lines);
[19]699
[42]700Parse the file or string. Returns the parser object. This method,
701along with all of its C<parse_*> siblings, may be called as a class
702method to construct a new object.
[19]703
704=head2 parse_file
705
706    $parser->parse_file($src_file);
707   
[7]708    # or as a class method
[16]709    my $parser = Text::FormBuilder->parse($src_file);
[7]710
711=head2 parse_text
712
[16]713    $parser->parse_text($src);
714
[19]715Parse the given C<$src> text. Returns the parser object.
[16]716
[42]717=head2 parse_array
718
719    $parser->parse_array(@lines);
720
721Concatenates and parses C<@lines>. Returns the parser object.
722
[1]723=head2 build
724
[12]725    $parser->build(%options);
[7]726
[12]727Builds the CGI::FormBuilder object. Options directly used by C<build> are:
728
729=over
730
[19]731=item C<form_only>
[12]732
733Only uses the form portion of the template, and omits the surrounding html,
[19]734title, author, and the standard footer. This does, however, include the
735description as specified with the C<!description> directive.
[12]736
[30]737=item C<css>, C<extra_css>
738
739These options allow you to tell Text::FormBuilder to use different
740CSS styles for the built in template. A value given a C<css> will
741replace the existing CSS, and a value given as C<extra_css> will be
742appended to the CSS. If both options are given, then the CSS that is
743used will be C<css> concatenated with C<extra_css>.
744
[38]745If you want to use an external stylesheet, a quick way to get this is
746to set the C<css> parameter to import your file:
747
748    css => '@import(my_external_stylesheet.css);'
749
[72]750=item C<external_css>
751
752If you want to use multiple external stylesheets, or an external stylesheet
753in conojunction with the default styles, use the C<external_css> option:
754
755    # single external sheet
756    external_css => 'my_styles.css'
757   
758    # mutliple sheets
759    external_css => [
760        'my_style_A.css',
761        'my_style_B.css',
762    ]
763
[34]764=item C<messages>
765
766This works the same way as the C<messages> parameter to
767C<< CGI::FormBuilder->new >>; you can provide either a hashref of messages
768or a filename.
769
770The default messages used by Text::FormBuilder are:
771
772    text_author       Created by %s
773    text_madewith     Made with %s version %s
774    text_required     (Required fields are marked in <strong>bold</strong>.)
775    text_invalid      Missing or invalid value.
776
777Any messages you set here get passed on to CGI::FormBuilder, which means
778that you should be able to put all of your customization messages in one
779big file.
780
781=item C<charset>
782
783Sets the character encoding for the generated page. The default is ISO-8859-1.
784
[12]785=back
786
787All other options given to C<build> are passed on verbatim to the
788L<CGI::FormBuilder> constructor. Any options given here override the
789defaults that this module uses.
790
[16]791The C<form>, C<write>, and C<write_module> methods will all call
792C<build> with no options for you if you do not do so explicitly.
793This allows you to say things like this:
794
795    my $form = Text::FormBuilder->new->parse('formspec.txt')->form;
796
797However, if you need to specify options to C<build>, you must call it
798explictly after C<parse>.
799
[7]800=head2 form
801
802    my $form = $parser->form;
803
[16]804Returns the L<CGI::FormBuilder> object. Remember that you can modify
805this object directly, in order to (for example) dynamically populate
806dropdown lists or change input types at runtime.
[7]807
[1]808=head2 write
809
[7]810    $parser->write($out_file);
811    # or just print to STDOUT
812    $parser->write;
813
[29]814Calls C<render> on the FormBuilder form, and either writes the resulting
815HTML to a file, or to STDOUT if no filename is given.
[7]816
[12]817=head2 write_module
818
[70]819I<B<Note:> The code output from the C<write_*> methods may be in flux for
820the next few versions, as I coordinate with the B<FormBuilder> project.>
821
[16]822    $parser->write_module($package, $use_tidy);
[12]823
824Takes a package name, and writes out a new module that can be used by your
825CGI script to render the form. This way, you only need CGI::FormBuilder on
826your server, and you don't have to parse the form spec each time you want
[74]827to display your form. The generated module is a subclass of L<CGI::FormBuilder>,
[77]828that will pass along any constructor arguments to FormBuilder, and set up
[74]829the fields for you.
[12]830
[16]831First, you parse the formspec and write the module, which you can do as a one-liner:
832
[19]833    $ perl -MText::FormBuilder -e"Text::FormBuilder->parse('formspec.txt')->write_module('My::Form')"
[16]834
[74]835And then, in your CGI script, use the new module:
[16]836
[12]837    #!/usr/bin/perl -w
838    use strict;
839   
840    use CGI;
[19]841    use My::Form;
[12]842   
843    my $q = CGI->new;
[74]844    my $form = My::Form->new;
[12]845   
846    # do the standard CGI::FormBuilder stuff
847    if ($form->submitted && $form->validate) {
848        # process results
849    } else {
850        print $q->header;
851        print $form->render;
852    }
853
[16]854If you pass a true value as the second argument to C<write_module>, the parser
855will run L<Perl::Tidy> on the generated code before writing the module file.
856
[19]857    # write tidier code
858    $parser->write_module('My::Form', 1);
859
[39]860=head2 write_script
861
862    $parser->write_script($filename, $use_tidy);
863
864If you don't need the reuseability of a separate module, you can have
865Text::FormBuilder write the form object to a script for you, along with
866the simplest framework for using it, to which you can add your actual
867form processing code.
868
869The generated script looks like this:
870
[74]871    #!/usr/bin/perl
872    use strict;
873    use warnings;
[39]874   
[74]875    use CGI::FormBuilder;
876   
877    my $form = CGI::FormBuilder->new(
878        # lots of stuff here...
879    );
880   
881    # ...and your field setup subs are here
882    $form->field(name => '...');
883       
884    unless ($form->submitted && $form->validate) {
885        print $form->render;
886    } else {
887        # do something with the entered data
[39]888    }
889
890Like C<write_module>, you can optionally pass a true value as the second
891argument to have Perl::Tidy make the generated code look nicer.
892
[7]893=head2 dump
894
[16]895Uses L<YAML> to print out a human-readable representation of the parsed
[7]896form spec.
897
[46]898=head1 EXPORTS
899
900There is one exported function, C<create_form>, that is intended to ``do the
901right thing'' in simple cases.
902
903=head2 create_form
904
905    # get a CGI::FormBuilder object
906    my $form = create_form($source, $options, $destination);
907   
908    # or just write the form immediately
909    create_form($source, $options, $destination);
910
911C<$source> accepts any of the types of arguments that C<parse> does. C<$options>
912is a hashref of options that should be passed to C<build>. Finally, C<$destination>
913is a simple scalar that determines where and what type of output C<create_form>
914should generate.
915
916    /\.pm$/             ->write_module($destination)
917    /\.(cgi|pl)$/       ->write_script($destination)
918    everything else     ->write($destination)
919
920For anything more than simple, one-off cases, you are usually better off using the
921object-oriented interface, since that gives you more control over things.
922
[33]923=head1 DEFAULTS
924
925These are the default settings that are passed to C<< CGI::FormBuilder->new >>:
926
927    method => 'GET'
928    keepextras => 1
929
930Any of these can be overriden by the C<build> method:
931
932    # use POST instead
933    $parser->build(method => 'POST')->write;
934
[1]935=head1 LANGUAGE
936
[74]937    # name field_size growable label hint type other default option_list validate
938   
[19]939    field_name[size]|descriptive label[hint]:type=default{option1[display string],...}//validate
[1]940   
941    !title ...
942   
[12]943    !author ...
944   
[16]945    !description {
946        ...
947    }
948   
[42]949    !pattern NAME /regular expression/
[16]950   
[42]951    !list NAME {
[7]952        option1[display string],
953        option2[display string],
[1]954        ...
955    }
[12]956   
[42]957    !group NAME {
958        field1
959        field2
960        ...
961    }
962   
[29]963    !section id heading
964   
[12]965    !head ...
[50]966   
967    !note {
968        ...
969    }
[80]970   
[81]971    !submit label, label 2, ...
972   
973    !reset label
[1]974
975=head2 Directives
976
977=over
978
979=item C<!pattern>
980
[12]981Defines a validation pattern.
982
[1]983=item C<!list>
984
[12]985Defines a list for use in a C<radio>, C<checkbox>, or C<select> field.
986
[42]987=item C<!group>
988
989Define a named group of fields that are displayed all on one line. Use with
990the C<!field> directive.
991
992=item C<!field>
993
[77]994B<DEPRECATED> Include a named instance of a group defined with C<!group>.
[42]995
[1]996=item C<!title>
997
[50]998Title of the form.
999
[7]1000=item C<!author>
1001
[50]1002Author of the form.
1003
[16]1004=item C<!description>
1005
[19]1006A brief description of the form. Suitable for special instructions on how to
1007fill out the form.
1008
[29]1009=item C<!section>
1010
1011Starts a new section. Each section has its own heading and id, which are
1012written by default into spearate tables.
1013
[12]1014=item C<!head>
1015
1016Inserts a heading between two fields. There can only be one heading between
1017any two fields; the parser will warn you if you try to put two headings right
1018next to each other.
1019
[50]1020=item C<!note>
1021
1022A text note that can be inserted as a row in the form. This is useful for
1023special instructions at specific points in a long form.
1024
[80]1025=item C<!submit>
1026
1027A list of one or more submit button labels in a comma-separated list. Each label
1028is a L<string|/Strings>. Multiple instances of this directive may be used; later
1029lists are simply appended to the earlier lists. All the submit buttons are
1030rendered together at the bottom of the form. See L<CGI::FormBuilder> for an
1031explanation of how the multiple submit buttons work together in a form.
1032
[81]1033To disable the display of any submit button, use C<!submit 0>
1034
1035=item C<!reset>
1036
1037The label for the a reset button at the end of the form. No reset button will be
1038rendered unless you use this directive.
1039
[1]1040=back
1041
[74]1042=head2 Strings
[1]1043
[24]1044First, a note about multiword strings in the fields. Anywhere where it says
1045that you may use a multiword string, this means that you can do one of two
1046things. For strings that consist solely of alphanumeric characters (i.e.
1047C<\w+>) and spaces, the string will be recognized as is:
[1]1048
[24]1049    field_1|A longer label
1050
1051If you want to include non-alphanumerics (e.g. punctuation), you must
1052single-quote the string:
1053
1054    field_2|'Dept./Org.'
1055
1056To include a literal single-quote in a single-quoted string, escape it with
1057a backslash:
1058
1059    field_3|'\'Official\' title'
1060
[71]1061Quoted strings are also how you can set the label for a field to be blank:
1062
1063    unlabeled_field|''
1064
[74]1065=head2 Fields
[24]1066
[74]1067Form fields are each described on a single line. The simplest field is
1068just a name (which cannot contain any whitespace):
1069
[19]1070    color
1071
1072This yields a form with one text input field of the default size named `color'.
[74]1073The generated label for this field would be ``Color''. To add a longer or more
[34]1074descriptive label, use:
[19]1075
1076    color|Favorite color
1077
[34]1078The descriptive label can be a multiword string, as described above. So if you
1079want punctuation in the label, you should single quote it:
[19]1080
[34]1081    color|'Fav. color'
1082
[19]1083To use a different input type:
1084
1085    color|Favorite color:select{red,blue,green}
1086
1087Recognized input types are the same as those used by CGI::FormBuilder:
1088
1089    text        # the default
1090    textarea
[23]1091    password
1092    file
1093    checkbox
1094    radio
[19]1095    select
[23]1096    hidden
[19]1097    static
1098
[21]1099To change the size of the input field, add a bracketed subscript after the
1100field name (but before the descriptive label):
[19]1101
[21]1102    # for a single line field, sets size="40"
1103    title[40]:text
1104   
1105    # for a multiline field, sets rows="4" and cols="30"
1106    description[4,30]:textarea
1107
[56]1108To also set the C<maxlength> attribute for text fields, add a C<!> after
1109the size:
1110
1111    # ensure that all titles entered are 40 characters or less
1112    title[40!]:text
1113
1114This currently only works for single line text fields.
[61]1115
1116To create a growable field, add a C<*> after the name (and size, if
1117given). Growable fields have a button that allows the user to add a
1118copy of the field input. Currently, this only works for C<text> and
[64]1119C<file> fields, and you must have L<CGI::FormBuilder> 3.02 or higher.
1120Growable fields also require JavaScript to function correctly.
[61]1121
1122    # you can have as many people as you like
1123    person*:text
1124
[80]1125To set a limit to the maximum number of inputs a field can grow to, add
1126a number after the C<*>:
1127
1128    # allow up to 5 musicians
1129    musician*5:text
1130
[74]1131To create a C<radio> or C<select> field that includes an "other" option,
1132append the string C<+other> to the field type:
1133
1134    position:select+other
1135
1136Or, to let FormBuilder decide whether to use radio buttons or a dropdown:
1137
1138    position+other
1139
1140Like growable fields, 'other' fields require FormBuilder 3.02 or higher.
1141
[21]1142For the input types that can have options (C<select>, C<radio>, and
1143C<checkbox>), here's how you do it:
1144
1145    color|Favorite color:select{red,blue,green}
1146
[24]1147Values are in a comma-separated list of single words or multiword strings
1148inside curly braces. Whitespace between values is irrelevant.
[21]1149
[26]1150To add more descriptive display text to a value in a list, add a square-bracketed
[19]1151``subscript,'' as in:
1152
1153    ...:select{red[Scarlet],blue[Azure],green[Olive Drab]}
1154
[1]1155If you have a list of options that is too long to fit comfortably on one line,
[26]1156you should use the C<!list> directive:
[1]1157
[19]1158    !list MONTHS {
1159        1[January],
1160        2[February],
1161        3[March],
1162        # and so on...
1163    }
1164   
1165    month:select@MONTHS
1166
[26]1167If you want to have a single checkbox (e.g. for a field that says ``I want to
1168recieve more information''), you can just specify the type as checkbox without
1169supplying any options:
1170
1171    moreinfo|I want to recieve more information:checkbox
1172
[30]1173In this case, the label ``I want to recieve more information'' will be
1174printed to the right of the checkbox.
[26]1175
[19]1176You can also supply a default value to the field. To get a default value of
1177C<green> for the color field:
1178
1179    color|Favorite color:select=green{red,blue,green}
1180
[24]1181Default values can also be either single words or multiword strings.
1182
[19]1183To validate a field, include a validation type at the end of the field line:
1184
1185    email|Email address//EMAIL
1186
[21]1187Valid validation types include any of the builtin defaults from L<CGI::FormBuilder>,
[19]1188or the name of a pattern that you define with the C<!pattern> directive elsewhere
1189in your form spec:
1190
1191    !pattern DAY /^([1-3][0-9])|[1-9]$/
1192   
1193    last_day//DAY
1194
1195If you just want a required value, use the builtin validation type C<VALUE>:
1196
1197    title//VALUE
1198
[24]1199By default, adding a validation type to a field makes that field required. To
1200change this, add a C<?> to the end of the validation type:
1201
1202    contact//EMAIL?
1203
1204In this case, you would get a C<contact> field that was optional, but if it
1205were filled in, would have to validate as an C<EMAIL>.
1206
[42]1207=head2 Field Groups
1208
1209You can define groups of fields using the C<!group> directive:
1210
1211    !group DATE {
1212        month:select@MONTHS//INT
1213        day[2]//INT
1214        year[4]//INT
1215    }
1216
1217You can then include instances of this group using the C<!field> directive:
1218
1219    !field %DATE birthday
1220
1221This will create a line in the form labeled ``Birthday'' which contains
1222a month dropdown, and day and year text entry fields. The actual input field
1223names are formed by concatenating the C<!field> name (e.g. C<birthday>) with
1224the name of the subfield defined in the group (e.g. C<month>, C<day>, C<year>).
1225Thus in this example, you would end up with the form fields C<birthday_month>,
1226C<birthday_day>, and C<birthday_year>.
1227
[63]1228You can also use groups in normal field lines:
[73]1229
[63]1230    birthday|Your birthday:DATE
1231
1232The only (currently) supported pieces of a fieldspec that may be used with a
[73]1233group in this notation are name, label, and hint.
[63]1234
[1]1235=head2 Comments
1236
1237    # comment ...
1238
[73]1239Any line beginning with a C<#> is considered a comment. Comments can also appear
1240after any field line. They I<cannot> appear between items in a C<!list>, or on
1241the same line as any of the directives.
[1]1242
[7]1243=head1 TODO
1244
[66]1245=head2 Documentation/Tests
[52]1246
[64]1247Document use of the parser as a standalone module
1248
[72]1249Make sure that the docs match the generated code.
1250
[66]1251Better tests!
1252
1253=head2 Language/Parser
1254
[80]1255Debug flag (that sets/unsets C<$::RD_TRACE> in the parser)
1256
[77]1257Make sure that multiple runs of the parser don't share data.
[74]1258
[80]1259Warn/suggest using the C<!submit> directive if some uses C<foo:submit>?
1260
[73]1261Set FB constructor options directly in the formspec (via a C<!fb> or similar
1262directive). The major issue here would be what format to use to allow for
1263array/hash refs.
[52]1264
1265Pieces that wouldn't make sense in a group field: size, row/col, options,
1266validate. These should cause C<build> to emit a warning before ignoring them.
1267
[66]1268C<!include> directive to include external formspec files
1269
1270=head2 Code generation/Templates
1271
[77]1272Expose some of the currently private functions to be able to get the generated
1273code text directly, without printing.
1274
[70]1275Revise the generated form constructing code to use the C<fieldopts>
1276option to C<< FB->new >>; will require FB 3.02 to run.
1277
[80]1278Better integration with L<CGI::FormBuilder>'s templating system; rely on the
1279FB messages instead of trying to make our own.
[66]1280
[31]1281Allow for custom wrappers around the C<form_template>
1282
[30]1283Maybe use HTML::Template instead of Text::Template for the built in template
[28]1284(since CGI::FormBuilder users may be more likely to already have HTML::Template)
1285
[23]1286=head1 BUGS
1287
[42]1288Creating two $parsers in the same script causes the second one to get the data
1289from the first one.
[26]1290
[42]1291I'm sure there are more in there, I just haven't tripped over any new ones lately. :-)
1292
1293Suggestions on how to improve the (currently tiny) test suite would be appreciated.
1294
[1]1295=head1 SEE ALSO
1296
[50]1297L<http://textformbuilder.berlios.de>
[1]1298
[50]1299L<CGI::FormBuilder>, L<http://formbuilder.org>
1300
[23]1301=head1 THANKS
1302
[26]1303Thanks to eszpee for pointing out some bugs in the default value parsing,
1304as well as some suggestions for i18n/l10n and splitting up long forms into
[29]1305sections.
[23]1306
[61]1307And of course, to Nathan Wiger, for giving use CGI::FormBuilder in the
1308first place. Thanks Nate!
1309
[16]1310=head1 AUTHOR
1311
[34]1312Peter Eichman C<< <peichman@cpan.org> >>
[16]1313
1314=head1 COPYRIGHT AND LICENSE
1315
[53]1316Copyright E<copy>2004-2005 by Peter Eichman.
[16]1317
1318This program is free software; you can redistribute it and/or
1319modify it under the same terms as Perl itself.
1320
[1]1321=cut
Note: See TracBrowser for help on using the repository browser.