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

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

using <fieldset> instead of <h2> tags for dividing forms up into sections

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