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

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

addded an external_css option to include external stylesheets

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