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

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

added !fb directive to hold FB parameters as YAML serialized values
updated and expanded the documentation

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