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

Last change on this file since 80 was 80, checked in by peichman, 20 years ago

added a !submit directive to rename the submit button or to have multiple submit buttons
upped version number

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