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

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

added support for the 'multiple' attribute on fields

File size: 40.5 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; }
[82]38.label { font-weight: normal; text-align: right; vertical-align: top; }
[30]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',
[83]49    text_required => 'Fields that are <strong>highlighted</strong> are required.',
[34]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    );
[82]365}
[39]366
[82]367sub as_module {
[39]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$//;
[82]374
375    # auto-build
376    $self->build unless $self->{built};
377
[69]378    my $form_options = $self->_form_options_code;
379    my $field_setup = $self->_field_setup_code('$self');
[39]380   
[69]381    # old style of module
382    # TODO: how to keep this (as deprecated method)
383    my $old_module = <<END;
[12]384package $package;
385use strict;
386use warnings;
387
388use CGI::FormBuilder;
389
[15]390sub get_form {
[39]391    my \$q = shift;
392
[69]393    my \$self = CGI::FormBuilder->new(
394        $form_options,
395        \@_,
396    );
[12]397   
[69]398    $field_setup
399   
400    return \$self;
[12]401}
402
403# module return
4041;
405END
[23]406
[69]407    # new style of module
408    my $module = <<END;
409package $package;
410use strict;
411use warnings;
412
413use base qw(CGI::FormBuilder);
414
415sub new {
416    my \$invocant = shift;
417    my \$class = ref \$invocant || \$invocant;
418   
419    my \$self = CGI::FormBuilder->new(
420        $form_options,
421        \@_,
422    );
423   
424    $field_setup
425   
426    # re-bless into this class
427    bless \$self, \$class;
428}
429
430# module return
4311;
432END
[82]433
434    $module = _tidy_code($module, $use_tidy) if $use_tidy;
435   
436    return $module;
437}
438
439sub write_module {
[83]440    my ($self, $package, $use_tidy) = @_;
[82]441   
442    my $module = $self->as_module($package, $use_tidy);
443   
[83]444    my $outfile = (split(/::/, $package))[-1];
445    $outfile .= '.pm' unless $outfile =~ /\.pm$/;
446    _write_output_file($module, $outfile);
[39]447    return $self;
448}
449
[82]450sub as_script {
451    my ($self, $use_tidy) = @_;
[39]452   
[82]453    # auto-build
454    $self->build unless $self->{built};
455   
[69]456    my $form_options = $self->_form_options_code;
457    my $field_setup = $self->_field_setup_code('$form');
458
[39]459    my $script = <<END;
460#!/usr/bin/perl
461use strict;
462use warnings;
463
464use CGI::FormBuilder;
465
[69]466my \$form = CGI::FormBuilder->new(
467    $form_options
468);
[39]469
[69]470$field_setup
[39]471   
472unless (\$form->submitted && \$form->validate) {
473    print \$form->render;
474} else {
475    # do something with the entered data
476}
477END
[82]478    $script = _tidy_code($script, $use_tidy) if $use_tidy;
[39]479   
[82]480    return $script;
481}
482   
483sub write_script {
484    my ($self, $script_name, $use_tidy) = @_;
485
486    croak '[' . (caller(0))[3] . '] Expecting a script name' unless $script_name;
487
488    my $script = $self->as_script($use_tidy);
489   
490    _write_output_file($script, $script_name);   
[39]491    return $self;
492}
493
[82]494sub _tidy_code {
495    my ($source_code, $use_tidy) = @_;
496    eval 'use Perl::Tidy';
497    carp '[' . (caller(0))[3] . "] Can't tidy the code: $@" and return $source_code if $@;
498   
499    # use the options string only if it begins with '_'
500    my $options = ($use_tidy =~ /^-/) ? $use_tidy : undef;
501   
502    my $tidy_code;
503    Perl::Tidy::perltidy(source => \$source_code, destination => \$tidy_code, argv => $options || $TIDY_OPTIONS);
504   
505    return $tidy_code;
506}
507
508
[39]509sub _write_output_file {
[82]510    my ($source_code, $outfile) = @_;   
511    open OUT, "> $outfile" or croak '[' . (caller(1))[3] . "] Can't open $outfile for writing: $!";
512    print OUT $source_code;
513    close OUT;
[12]514}
515
[39]516
[16]517sub form {
518    my $self = shift;
519   
520    # automatically call build if needed to
521    # allow the new->parse->write shortcut
522    $self->build unless $self->{built};
[1]523
[16]524    return $self->{form};
525}
526
[12]527sub _form_template {
[33]528    my $self = shift;
[34]529    my $msg_required = $self->{build_options}{messages}{text_required};
530    my $msg_invalid = $self->{build_options}{messages}{text_invalid};
531    return q{<% $description ? qq[<p id="description">$description</p>] : '' %>
532<% (grep { $_->{required} } @fields) ? qq[<p id="instructions">} . $msg_required . q{</p>] : '' %>
[12]533<% $start %>
[23]534<%
535    # drop in the hidden fields here
536    $OUT = join("\n", map { $$_{field} } grep { $$_{type} eq 'hidden' } @fields);
[34]537%>} .
538q[
[29]539<%
540    SECTION: while (my $section = shift @sections) {
[67]541        $OUT .= qq[<fieldset>\n];
542        $OUT .= qq[  <legend>$$section{head}</legend>] if $$section{head};
[30]543        $OUT .= qq[<table id="] . ($$section{id} || '_default') . qq[">\n];
[67]544        #$OUT .= qq[  <caption><h2 class="sectionhead">$$section{head}</h2></caption>] if $$section{head};
[29]545        TABLE_LINE: for my $line (@{ $$section{lines} }) {
546            if ($$line[0] eq 'head') {
[71]547                $OUT .= qq[  <tr><th class="subhead" colspan="2"><h2>$$line[1]</h2></th></tr>\n]
[50]548            } elsif ($$line[0] eq 'note') {
549                $OUT .= qq[  <tr><td class="note" colspan="2">$$line[1]</td></tr>\n]
[29]550            } elsif ($$line[0] eq 'field') {
[42]551                local $_ = $field{$$line[1]};
[34]552               
[29]553                # skip hidden fields in the table
554                next TABLE_LINE if $$_{type} eq 'hidden';
555               
556                $OUT .= $$_{invalid} ? qq[  <tr class="invalid">] : qq[  <tr>];
[30]557               
558                # special case single value checkboxes
559                if ($$_{type} eq 'checkbox' && @{ $$_{options} } == 1) {
[82]560                    $OUT .= qq[<td></td>];
[30]561                } else {
[83]562                    $OUT .= '<td class="label">' . ($$_{required} ? qq[<strong class="required">$$_{label}</strong>] : "$$_{label}") . '</td>';
[30]563                }
[33]564               
565                # mark invalid fields
[29]566                if ($$_{invalid}) {
[81]567                    $OUT .= qq[<td>$$_{field} <span class="comment">$$_{comment}</span> $$_{error}</td>];
[29]568                } else {
[73]569                    $OUT .= qq[<td>$$_{field} <span class="comment">$$_{comment}</span></td>];
[29]570                }
[33]571               
[30]572                $OUT .= qq[</tr>\n];
573               
[29]574            } elsif ($$line[0] eq 'group') {
[42]575                my @group_fields = map { $field{$_} } map { $$_{name} } @{ $$line[1]{group} };
[29]576                $OUT .= (grep { $$_{invalid} } @group_fields) ? qq[  <tr class="invalid">\n] : qq[  <tr>\n];
577               
[82]578                $OUT .= '    <td class="label">';
[83]579                $OUT .= (grep { $$_{required} } @group_fields) ? qq[<strong class="required">$$line[1]{label}</strong>] : "$$line[1]{label}";
[82]580                $OUT .= qq[</td>\n];
[29]581               
[59]582                $OUT .= qq[    <td><span class="fieldgroup">];
[29]583                $OUT .= join(' ', map { qq[<small class="sublabel">$$_{label}</small> $$_{field} $$_{comment}] } @group_fields);
[81]584                if (my @invalid = grep { $$_{invalid} } @group_fields) {
585                    $OUT .= ' ' . join('; ', map { $$_{error} } @invalid);
586                }               
[73]587                $OUT .= qq[ <span class="comment">$$line[1]{comment}</span></span></td>\n];
[29]588                $OUT .= qq[  </tr>\n];
589            }   
[21]590        }
[29]591        # close the table if there are sections remaining
592        # but leave the last one open for the submit button
[67]593        if (@sections) {
594            $OUT .= qq[</table>\n];
595            $OUT .= qq[</fieldset>\n];
596        }
[29]597    }
598%>
[81]599  <tr><th></th><td style="padding-top: 1em;"><% $submit %> <% $reset %></td></tr>
[12]600</table>
[67]601</fieldset>
[12]602<% $end %>
603];
604}
605
[34]606# usage: $self->_pre_template($css, $charset)
[33]607sub _pre_template {
[12]608    my $self = shift;
[30]609    my $css = shift || $DEFAULT_CSS;
[34]610    my $charset = shift || $DEFAULT_CHARSET;
611    my $msg_author = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_author}) . '", $author)';
[33]612    return 
[12]613q[<html>
[1]614<head>
[34]615  <meta http-equiv="Content-Type" content="text/html; charset=] . $charset . q[" />
[1]616  <title><% $title %><% $author ? ' - ' . ucfirst $author : '' %></title>
[33]617  <style type="text/css">
[42]618] . $css . q[  </style>
[33]619  <% $jshead %>
[1]620</head>
621<body>
622
623<h1><% $title %></h1>
[34]624<% $author ? qq[<p id="author">] . ] . $msg_author . q{ . q[</p>] : '' %>
625};
[33]626}
627
628sub _post_template {
[34]629    my $self = shift;
630    my $msg_madewith = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_madewith}) .
631        '", q[<a href="http://formbuilder.org/">CGI::FormBuilder</a>], CGI::FormBuilder->VERSION)';
632   
633    return qq[<hr />
[1]634<div id="footer">
[34]635  <p id="creator"><% $msg_madewith %></p>
[1]636</div>
637</body>
638</html>
639];
640}
641
[42]642# usage: $self->_template($css, $charset)
[33]643sub _template {
644    my $self = shift;
[34]645    return $self->_pre_template(@_) . $self->_form_template . $self->_post_template;
[33]646}
647
[7]648sub dump { 
649    eval "use YAML;";
650    unless ($@) {
651        print YAML::Dump(shift->{form_spec});
652    } else {
[53]653        warn '[' . (caller(0))[3] . "] Can't dump form spec structure using YAML: $@";
[7]654    }
655}
[1]656
657
658# module return
6591;
660
661=head1 NAME
662
[21]663Text::FormBuilder - Create CGI::FormBuilder objects from simple text descriptions
[1]664
665=head1 SYNOPSIS
666
[16]667    use Text::FormBuilder;
668   
[1]669    my $parser = Text::FormBuilder->new;
670    $parser->parse($src_file);
671   
[16]672    # returns a new CGI::FormBuilder object with
673    # the fields from the input form spec
[7]674    my $form = $parser->form;
[19]675   
676    # write a My::Form module to Form.pm
677    $parser->write_module('My::Form');
[1]678
[23]679=head1 REQUIRES
680
[56]681L<Parse::RecDescent>,
682L<CGI::FormBuilder>,
683L<Text::Template>,
684L<Class::Base>
[23]685
[1]686=head1 DESCRIPTION
687
[38]688This module is intended to extend the idea of making it easy to create
689web forms by allowing you to describe them with a simple langauge. These
690I<formspecs> are then passed through this module's parser and converted
691into L<CGI::FormBuilder> objects that you can easily use in your CGI
692scripts. In addition, this module can generate code for standalone modules
693which allow you to separate your form design from your script code.
694
695A simple formspec looks like this:
696
697    name//VALUE
698    email//EMAIL
699    langauge:select{English,Spanish,French,German}
700    moreinfo|Send me more information:checkbox
701    interests:checkbox{Perl,karate,bass guitar}
702
703This will produce a required C<name> test field, a required C<email> text
704field that must look like an email address, an optional select dropdown
705field C<langauge> with the choices English, Spanish, French, and German,
706an optional C<moreinfo> checkbox labeled ``Send me more information'', and
707finally a set of checkboxes named C<interests> with the choices Perl,
708karate, and bass guitar.
709
[46]710=head1 METHODS
711
[1]712=head2 new
713
[38]714    my $parser = Text::FormBuilder->new;
715
[1]716=head2 parse
717
[42]718    # parse a file (regular scalar)
[19]719    $parser->parse($filename);
[7]720   
[19]721    # or pass a scalar ref for parse a literal string
722    $parser->parse(\$string);
[42]723   
724    # or an array ref to parse lines
725    $parser->parse(\@lines);
[19]726
[42]727Parse the file or string. Returns the parser object. This method,
728along with all of its C<parse_*> siblings, may be called as a class
729method to construct a new object.
[19]730
731=head2 parse_file
732
733    $parser->parse_file($src_file);
734   
[7]735    # or as a class method
[16]736    my $parser = Text::FormBuilder->parse($src_file);
[7]737
738=head2 parse_text
739
[16]740    $parser->parse_text($src);
741
[19]742Parse the given C<$src> text. Returns the parser object.
[16]743
[42]744=head2 parse_array
745
746    $parser->parse_array(@lines);
747
748Concatenates and parses C<@lines>. Returns the parser object.
749
[1]750=head2 build
751
[12]752    $parser->build(%options);
[7]753
[12]754Builds the CGI::FormBuilder object. Options directly used by C<build> are:
755
756=over
757
[19]758=item C<form_only>
[12]759
760Only uses the form portion of the template, and omits the surrounding html,
[19]761title, author, and the standard footer. This does, however, include the
762description as specified with the C<!description> directive.
[12]763
[30]764=item C<css>, C<extra_css>
765
766These options allow you to tell Text::FormBuilder to use different
767CSS styles for the built in template. A value given a C<css> will
768replace the existing CSS, and a value given as C<extra_css> will be
769appended to the CSS. If both options are given, then the CSS that is
770used will be C<css> concatenated with C<extra_css>.
771
[38]772If you want to use an external stylesheet, a quick way to get this is
773to set the C<css> parameter to import your file:
774
775    css => '@import(my_external_stylesheet.css);'
776
[72]777=item C<external_css>
778
779If you want to use multiple external stylesheets, or an external stylesheet
780in conojunction with the default styles, use the C<external_css> option:
781
782    # single external sheet
783    external_css => 'my_styles.css'
784   
785    # mutliple sheets
786    external_css => [
787        'my_style_A.css',
788        'my_style_B.css',
789    ]
790
[34]791=item C<messages>
792
793This works the same way as the C<messages> parameter to
794C<< CGI::FormBuilder->new >>; you can provide either a hashref of messages
795or a filename.
796
797The default messages used by Text::FormBuilder are:
798
799    text_author       Created by %s
800    text_madewith     Made with %s version %s
801    text_required     (Required fields are marked in <strong>bold</strong>.)
802    text_invalid      Missing or invalid value.
803
804Any messages you set here get passed on to CGI::FormBuilder, which means
805that you should be able to put all of your customization messages in one
806big file.
807
808=item C<charset>
809
810Sets the character encoding for the generated page. The default is ISO-8859-1.
811
[12]812=back
813
814All other options given to C<build> are passed on verbatim to the
815L<CGI::FormBuilder> constructor. Any options given here override the
816defaults that this module uses.
817
[16]818The C<form>, C<write>, and C<write_module> methods will all call
819C<build> with no options for you if you do not do so explicitly.
820This allows you to say things like this:
821
822    my $form = Text::FormBuilder->new->parse('formspec.txt')->form;
823
824However, if you need to specify options to C<build>, you must call it
825explictly after C<parse>.
826
[7]827=head2 form
828
829    my $form = $parser->form;
830
[16]831Returns the L<CGI::FormBuilder> object. Remember that you can modify
832this object directly, in order to (for example) dynamically populate
833dropdown lists or change input types at runtime.
[7]834
[1]835=head2 write
836
[7]837    $parser->write($out_file);
838    # or just print to STDOUT
839    $parser->write;
840
[29]841Calls C<render> on the FormBuilder form, and either writes the resulting
842HTML to a file, or to STDOUT if no filename is given.
[7]843
[82]844=head2 as_module
845
846    my $module_code = $parser->as_module($package, $use_tidy);
847
[12]848=head2 write_module
849
[70]850I<B<Note:> The code output from the C<write_*> methods may be in flux for
851the next few versions, as I coordinate with the B<FormBuilder> project.>
852
[16]853    $parser->write_module($package, $use_tidy);
[12]854
855Takes a package name, and writes out a new module that can be used by your
856CGI script to render the form. This way, you only need CGI::FormBuilder on
857your server, and you don't have to parse the form spec each time you want
[74]858to display your form. The generated module is a subclass of L<CGI::FormBuilder>,
[77]859that will pass along any constructor arguments to FormBuilder, and set up
[74]860the fields for you.
[12]861
[16]862First, you parse the formspec and write the module, which you can do as a one-liner:
863
[19]864    $ perl -MText::FormBuilder -e"Text::FormBuilder->parse('formspec.txt')->write_module('My::Form')"
[16]865
[74]866And then, in your CGI script, use the new module:
[16]867
[12]868    #!/usr/bin/perl -w
869    use strict;
870   
871    use CGI;
[19]872    use My::Form;
[12]873   
874    my $q = CGI->new;
[74]875    my $form = My::Form->new;
[12]876   
877    # do the standard CGI::FormBuilder stuff
878    if ($form->submitted && $form->validate) {
879        # process results
880    } else {
881        print $q->header;
882        print $form->render;
883    }
884
[16]885If you pass a true value as the second argument to C<write_module>, the parser
886will run L<Perl::Tidy> on the generated code before writing the module file.
887
[19]888    # write tidier code
889    $parser->write_module('My::Form', 1);
890
[82]891If you set C<$use_tidy> to a string beginning with `-' C<write_module> will
892interpret C<$use_tidy> as the formatting option switches to pass to Perl::Tidy.
893
894=head2 as_script
895
896    my $script_code = $parser->as_script($use_tidy);
897
[39]898=head2 write_script
899
900    $parser->write_script($filename, $use_tidy);
901
902If you don't need the reuseability of a separate module, you can have
903Text::FormBuilder write the form object to a script for you, along with
904the simplest framework for using it, to which you can add your actual
905form processing code.
906
907The generated script looks like this:
908
[74]909    #!/usr/bin/perl
910    use strict;
911    use warnings;
[39]912   
[74]913    use CGI::FormBuilder;
914   
915    my $form = CGI::FormBuilder->new(
916        # lots of stuff here...
917    );
918   
919    # ...and your field setup subs are here
920    $form->field(name => '...');
921       
922    unless ($form->submitted && $form->validate) {
923        print $form->render;
924    } else {
925        # do something with the entered data
[39]926    }
927
928Like C<write_module>, you can optionally pass a true value as the second
929argument to have Perl::Tidy make the generated code look nicer.
930
[7]931=head2 dump
932
[16]933Uses L<YAML> to print out a human-readable representation of the parsed
[7]934form spec.
935
[46]936=head1 EXPORTS
937
938There is one exported function, C<create_form>, that is intended to ``do the
939right thing'' in simple cases.
940
941=head2 create_form
942
943    # get a CGI::FormBuilder object
944    my $form = create_form($source, $options, $destination);
945   
946    # or just write the form immediately
947    create_form($source, $options, $destination);
948
949C<$source> accepts any of the types of arguments that C<parse> does. C<$options>
950is a hashref of options that should be passed to C<build>. Finally, C<$destination>
951is a simple scalar that determines where and what type of output C<create_form>
952should generate.
953
954    /\.pm$/             ->write_module($destination)
955    /\.(cgi|pl)$/       ->write_script($destination)
956    everything else     ->write($destination)
957
958For anything more than simple, one-off cases, you are usually better off using the
959object-oriented interface, since that gives you more control over things.
960
[33]961=head1 DEFAULTS
962
963These are the default settings that are passed to C<< CGI::FormBuilder->new >>:
964
965    method => 'GET'
966    keepextras => 1
967
968Any of these can be overriden by the C<build> method:
969
970    # use POST instead
971    $parser->build(method => 'POST')->write;
972
[1]973=head1 LANGUAGE
974
[74]975    # name field_size growable label hint type other default option_list validate
976   
[19]977    field_name[size]|descriptive label[hint]:type=default{option1[display string],...}//validate
[1]978   
979    !title ...
980   
[12]981    !author ...
982   
[16]983    !description {
984        ...
985    }
986   
[42]987    !pattern NAME /regular expression/
[16]988   
[42]989    !list NAME {
[7]990        option1[display string],
991        option2[display string],
[1]992        ...
993    }
[12]994   
[42]995    !group NAME {
996        field1
997        field2
998        ...
999    }
1000   
[29]1001    !section id heading
1002   
[12]1003    !head ...
[50]1004   
1005    !note {
1006        ...
1007    }
[80]1008   
[81]1009    !submit label, label 2, ...
1010   
1011    !reset label
[1]1012
1013=head2 Directives
1014
1015=over
1016
1017=item C<!pattern>
1018
[12]1019Defines a validation pattern.
1020
[1]1021=item C<!list>
1022
[12]1023Defines a list for use in a C<radio>, C<checkbox>, or C<select> field.
1024
[42]1025=item C<!group>
1026
1027Define a named group of fields that are displayed all on one line. Use with
1028the C<!field> directive.
1029
1030=item C<!field>
1031
[77]1032B<DEPRECATED> Include a named instance of a group defined with C<!group>.
[42]1033
[1]1034=item C<!title>
1035
[50]1036Title of the form.
1037
[7]1038=item C<!author>
1039
[50]1040Author of the form.
1041
[16]1042=item C<!description>
1043
[19]1044A brief description of the form. Suitable for special instructions on how to
1045fill out the form.
1046
[29]1047=item C<!section>
1048
1049Starts a new section. Each section has its own heading and id, which are
1050written by default into spearate tables.
1051
[12]1052=item C<!head>
1053
1054Inserts a heading between two fields. There can only be one heading between
1055any two fields; the parser will warn you if you try to put two headings right
1056next to each other.
1057
[50]1058=item C<!note>
1059
1060A text note that can be inserted as a row in the form. This is useful for
1061special instructions at specific points in a long form.
1062
[80]1063=item C<!submit>
1064
1065A list of one or more submit button labels in a comma-separated list. Each label
1066is a L<string|/Strings>. Multiple instances of this directive may be used; later
1067lists are simply appended to the earlier lists. All the submit buttons are
1068rendered together at the bottom of the form. See L<CGI::FormBuilder> for an
1069explanation of how the multiple submit buttons work together in a form.
1070
[81]1071To disable the display of any submit button, use C<!submit 0>
1072
1073=item C<!reset>
1074
1075The label for the a reset button at the end of the form. No reset button will be
1076rendered unless you use this directive.
1077
[1]1078=back
1079
[74]1080=head2 Strings
[1]1081
[24]1082First, a note about multiword strings in the fields. Anywhere where it says
1083that you may use a multiword string, this means that you can do one of two
1084things. For strings that consist solely of alphanumeric characters (i.e.
1085C<\w+>) and spaces, the string will be recognized as is:
[1]1086
[24]1087    field_1|A longer label
1088
1089If you want to include non-alphanumerics (e.g. punctuation), you must
1090single-quote the string:
1091
1092    field_2|'Dept./Org.'
1093
1094To include a literal single-quote in a single-quoted string, escape it with
1095a backslash:
1096
1097    field_3|'\'Official\' title'
1098
[71]1099Quoted strings are also how you can set the label for a field to be blank:
1100
1101    unlabeled_field|''
1102
[74]1103=head2 Fields
[24]1104
[74]1105Form fields are each described on a single line. The simplest field is
1106just a name (which cannot contain any whitespace):
1107
[19]1108    color
1109
1110This yields a form with one text input field of the default size named `color'.
[74]1111The generated label for this field would be ``Color''. To add a longer or more
[34]1112descriptive label, use:
[19]1113
1114    color|Favorite color
1115
[34]1116The descriptive label can be a multiword string, as described above. So if you
1117want punctuation in the label, you should single quote it:
[19]1118
[34]1119    color|'Fav. color'
1120
[19]1121To use a different input type:
1122
1123    color|Favorite color:select{red,blue,green}
1124
1125Recognized input types are the same as those used by CGI::FormBuilder:
1126
1127    text        # the default
1128    textarea
[23]1129    password
1130    file
1131    checkbox
1132    radio
[19]1133    select
[23]1134    hidden
[19]1135    static
1136
[84]1137For multi-select fields, append a C<*> to the field type:
1138
1139    colors:select*
1140
[21]1141To change the size of the input field, add a bracketed subscript after the
1142field name (but before the descriptive label):
[19]1143
[21]1144    # for a single line field, sets size="40"
1145    title[40]:text
1146   
1147    # for a multiline field, sets rows="4" and cols="30"
1148    description[4,30]:textarea
1149
[56]1150To also set the C<maxlength> attribute for text fields, add a C<!> after
1151the size:
1152
1153    # ensure that all titles entered are 40 characters or less
1154    title[40!]:text
1155
1156This currently only works for single line text fields.
[61]1157
1158To create a growable field, add a C<*> after the name (and size, if
1159given). Growable fields have a button that allows the user to add a
1160copy of the field input. Currently, this only works for C<text> and
[64]1161C<file> fields, and you must have L<CGI::FormBuilder> 3.02 or higher.
1162Growable fields also require JavaScript to function correctly.
[61]1163
1164    # you can have as many people as you like
1165    person*:text
1166
[80]1167To set a limit to the maximum number of inputs a field can grow to, add
1168a number after the C<*>:
1169
1170    # allow up to 5 musicians
1171    musician*5:text
1172
[74]1173To create a C<radio> or C<select> field that includes an "other" option,
1174append the string C<+other> to the field type:
1175
1176    position:select+other
1177
1178Or, to let FormBuilder decide whether to use radio buttons or a dropdown:
1179
1180    position+other
1181
1182Like growable fields, 'other' fields require FormBuilder 3.02 or higher.
1183
[21]1184For the input types that can have options (C<select>, C<radio>, and
1185C<checkbox>), here's how you do it:
1186
1187    color|Favorite color:select{red,blue,green}
1188
[24]1189Values are in a comma-separated list of single words or multiword strings
1190inside curly braces. Whitespace between values is irrelevant.
[21]1191
[26]1192To add more descriptive display text to a value in a list, add a square-bracketed
[19]1193``subscript,'' as in:
1194
1195    ...:select{red[Scarlet],blue[Azure],green[Olive Drab]}
1196
[1]1197If you have a list of options that is too long to fit comfortably on one line,
[26]1198you should use the C<!list> directive:
[1]1199
[19]1200    !list MONTHS {
1201        1[January],
1202        2[February],
1203        3[March],
1204        # and so on...
1205    }
1206   
1207    month:select@MONTHS
1208
[26]1209If you want to have a single checkbox (e.g. for a field that says ``I want to
1210recieve more information''), you can just specify the type as checkbox without
1211supplying any options:
1212
1213    moreinfo|I want to recieve more information:checkbox
1214
[30]1215In this case, the label ``I want to recieve more information'' will be
1216printed to the right of the checkbox.
[26]1217
[19]1218You can also supply a default value to the field. To get a default value of
1219C<green> for the color field:
1220
1221    color|Favorite color:select=green{red,blue,green}
1222
[24]1223Default values can also be either single words or multiword strings.
1224
[19]1225To validate a field, include a validation type at the end of the field line:
1226
1227    email|Email address//EMAIL
1228
[21]1229Valid validation types include any of the builtin defaults from L<CGI::FormBuilder>,
[19]1230or the name of a pattern that you define with the C<!pattern> directive elsewhere
1231in your form spec:
1232
1233    !pattern DAY /^([1-3][0-9])|[1-9]$/
1234   
1235    last_day//DAY
1236
1237If you just want a required value, use the builtin validation type C<VALUE>:
1238
1239    title//VALUE
1240
[24]1241By default, adding a validation type to a field makes that field required. To
1242change this, add a C<?> to the end of the validation type:
1243
1244    contact//EMAIL?
1245
1246In this case, you would get a C<contact> field that was optional, but if it
1247were filled in, would have to validate as an C<EMAIL>.
1248
[42]1249=head2 Field Groups
1250
1251You can define groups of fields using the C<!group> directive:
1252
1253    !group DATE {
1254        month:select@MONTHS//INT
1255        day[2]//INT
1256        year[4]//INT
1257    }
1258
1259You can then include instances of this group using the C<!field> directive:
1260
1261    !field %DATE birthday
1262
1263This will create a line in the form labeled ``Birthday'' which contains
1264a month dropdown, and day and year text entry fields. The actual input field
1265names are formed by concatenating the C<!field> name (e.g. C<birthday>) with
1266the name of the subfield defined in the group (e.g. C<month>, C<day>, C<year>).
1267Thus in this example, you would end up with the form fields C<birthday_month>,
1268C<birthday_day>, and C<birthday_year>.
1269
[63]1270You can also use groups in normal field lines:
[73]1271
[63]1272    birthday|Your birthday:DATE
1273
1274The only (currently) supported pieces of a fieldspec that may be used with a
[73]1275group in this notation are name, label, and hint.
[63]1276
[1]1277=head2 Comments
1278
1279    # comment ...
1280
[73]1281Any line beginning with a C<#> is considered a comment. Comments can also appear
1282after any field line. They I<cannot> appear between items in a C<!list>, or on
1283the same line as any of the directives.
[1]1284
[7]1285=head1 TODO
1286
[66]1287=head2 Documentation/Tests
[52]1288
[64]1289Document use of the parser as a standalone module
1290
[72]1291Make sure that the docs match the generated code.
1292
[66]1293Better tests!
1294
1295=head2 Language/Parser
1296
[77]1297Make sure that multiple runs of the parser don't share data.
[74]1298
[80]1299Warn/suggest using the C<!submit> directive if some uses C<foo:submit>?
1300
[73]1301Set FB constructor options directly in the formspec (via a C<!fb> or similar
1302directive). The major issue here would be what format to use to allow for
1303array/hash refs.
[52]1304
1305Pieces that wouldn't make sense in a group field: size, row/col, options,
1306validate. These should cause C<build> to emit a warning before ignoring them.
1307
[66]1308C<!include> directive to include external formspec files
1309
1310=head2 Code generation/Templates
1311
[70]1312Revise the generated form constructing code to use the C<fieldopts>
1313option to C<< FB->new >>; will require FB 3.02 to run.
1314
[80]1315Better integration with L<CGI::FormBuilder>'s templating system; rely on the
1316FB messages instead of trying to make our own.
[66]1317
[31]1318Allow for custom wrappers around the C<form_template>
1319
[30]1320Maybe use HTML::Template instead of Text::Template for the built in template
[28]1321(since CGI::FormBuilder users may be more likely to already have HTML::Template)
1322
[23]1323=head1 BUGS
1324
[42]1325Creating two $parsers in the same script causes the second one to get the data
1326from the first one.
[26]1327
[42]1328I'm sure there are more in there, I just haven't tripped over any new ones lately. :-)
1329
1330Suggestions on how to improve the (currently tiny) test suite would be appreciated.
1331
[1]1332=head1 SEE ALSO
1333
[50]1334L<http://textformbuilder.berlios.de>
[1]1335
[50]1336L<CGI::FormBuilder>, L<http://formbuilder.org>
1337
[23]1338=head1 THANKS
1339
[26]1340Thanks to eszpee for pointing out some bugs in the default value parsing,
1341as well as some suggestions for i18n/l10n and splitting up long forms into
[29]1342sections.
[23]1343
[61]1344And of course, to Nathan Wiger, for giving use CGI::FormBuilder in the
1345first place. Thanks Nate!
1346
[16]1347=head1 AUTHOR
1348
[34]1349Peter Eichman C<< <peichman@cpan.org> >>
[16]1350
1351=head1 COPYRIGHT AND LICENSE
1352
[53]1353Copyright E<copy>2004-2005 by Peter Eichman.
[16]1354
1355This program is free software; you can redistribute it and/or
1356modify it under the same terms as Perl itself.
1357
[1]1358=cut
Note: See TracBrowser for help on using the repository browser.