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
RevLine 
[1]1package Text::FormBuilder;
2
3use strict;
4use warnings;
5
[53]6use base qw(Exporter Class::ParseText::Base);
[42]7use vars qw($VERSION @EXPORT);
[1]8
[66]9$VERSION = '0.10';
[42]10@EXPORT = qw(create_form);
[19]11
[63]12#$::RD_TRACE = 1;
13
[16]14use Carp;
[1]15use Text::FormBuilder::Parser;
16use CGI::FormBuilder;
17
[69]18use Data::Dumper;
19$Data::Dumper::Terse = 1;           # don't dump $VARn names
20$Data::Dumper::Quotekeys = 0;       # don't quote simple string keys
21
[28]22# the static default options passed to CGI::FormBuilder->new
[23]23my %DEFAULT_OPTIONS = (
24    method => 'GET',
25    keepextras => 1,
26);
27
[30]28# the built in CSS for the template
29my $DEFAULT_CSS = <<END;
30table { padding: 1em; }
[60]31td table { padding: 0; } /* exclude the inner checkbox tables */
[30]32#author, #footer { font-style: italic; }
33caption h2 { padding: .125em .5em; background: #ccc; text-align: left; }
[71]34fieldset { margin: 1em 0; border: none; border-top: 2px solid #999; }
[67]35legend { font-size: 1.25em; font-weight: bold; background: #ccc; padding: .125em .25em; border: 1px solid #666; }
[30]36th { text-align: left; }
[71]37th h2 { padding: .125em .5em; background: #eee; font-size: 1.25em; }
[30]38th.label { font-weight: normal; text-align: right; vertical-align: top; }
39td ul { list-style: none; padding-left: 0; margin-left: 0; }
[71]40.note { background: #eee; padding: .5em 1em; }
[30]41.sublabel { color: #999; }
42.invalid { background: red; }
43END
44
[34]45# default messages that can be localized
[33]46my %DEFAULT_MESSAGES = (
[34]47    text_author   => 'Created by %s',
48    text_madewith => 'Made with %s version %s',
49    text_required => '(Required fields are marked in <strong>bold</strong>.)',
50    text_invalid  => 'Missing or invalid value.',
[33]51);
52
[34]53my $DEFAULT_CHARSET = 'iso-8859-1';
54
[39]55# options to clean up the code with Perl::Tidy
56my $TIDY_OPTIONS = '-nolq -ci=4 -ce';
57
[42]58my $HTML_EXTS   = qr/\.html?$/;
[46]59my $MODULE_EXTS = qr/\.pm$/;
[42]60my $SCRIPT_EXTS = qr/\.(pl|cgi)$/;
61
62# superautomagical exported function
63sub create_form {
64    my ($source, $options, $destination) = @_;
65    my $parser = __PACKAGE__->parse($source);
66    $parser->build(%{ $options || {} });
67    if ($destination) {
68        if (ref $destination) {
[53]69            croak '[' . (caller(0))[3] . "] Don't know what to do with a ref for $destination";
70            #TODO: what DO ref dests mean?
[42]71        } else {
72            # write webpage, script, or module
[46]73            if ($destination =~ $MODULE_EXTS) {
[52]74                $parser->write_module($destination, 1);
[42]75            } elsif ($destination =~ $SCRIPT_EXTS) {
[52]76                $parser->write_script($destination, 1);
[42]77            } else {
[46]78                $parser->write($destination);
[42]79            }
80        }
81    } else {
82        defined wantarray ? return $parser->form : $parser->write;
83    }
84}
85
[53]86# subclass of Class::ParseText::Base
87sub init {
88    my $self = shift;
89    $self->{parser}         = Text::FormBuilder::Parser->new;
90    $self->{start_rule}     = 'form_spec';
91    $self->{ensure_newline} = 1;
[42]92    return $self;
93}
94
[33]95# this is where a lot of the magic happens
[1]96sub build {
97    my ($self, %options) = @_;
[12]98   
99    # our custom %options:
100    # form_only: use only the form part of the template
101    my $form_only = $options{form_only};
[33]102   
103    # css, extra_css: allow for custom inline stylesheets
[38]104    #   neat trick: css => '@import(my_external_stylesheet.css);'
[33]105    #   will let you use an external stylesheet
[34]106    #   CSS Hint: to get multiple sections to all line up their fields,
107    #   set a standard width for th.label
[72]108    # external_css: scalar for a single external stylesheet; array for
109    #   multiple sheets; prepended to the beginning of the CSS as @import
110    #   statetments
[30]111    my $css;
112    $css = $options{css} || $DEFAULT_CSS;
[72]113    if ($options{external_css}) {
114        my $ref = ref $options{external_css};
115        if ($ref eq 'ARRAY') {
116            # loop over the list of external sheets
117            my $external_sheets = join("\n", map { "\@import url($_);" } @{ $options{external_css} });
118            $css = "$external_sheets\n$css";
119        } elsif ($ref) {
120            croak '[' . (caller(0))[3] . "] Don't know how to handle $ref reference as an argument to external_css";
121        } else {
122            $css = "\@import url($options{external_css});\n$css";
123        }
124    }
[30]125    $css .= $options{extra_css} if $options{extra_css};
[12]126   
[33]127    # messages
[38]128    # code pulled (with modifications) from CGI::FormBuilder
[33]129    if ($options{messages}) {
130        # if its a hashref, we'll just pass it on to CGI::FormBuilder
131       
132        if (my $ref = ref $options{messages}) {
133            # hashref pass on to CGI::FormBuilder
134            croak "[Text::FormBuilder] Argument to 'messages' option must be a filename or hashref" unless $ref eq 'HASH';
135            while (my ($key,$value) = each %DEFAULT_MESSAGES) {
136                $options{messages}{$key} ||= $DEFAULT_MESSAGES{$key};
137            }
138        } else {
139            # filename, just *warn* on missing, and use defaults
140            if (-f $options{messages} && -r _ && open(MESSAGES, "< $options{messages}")) {
[34]141                $options{messages} = { %DEFAULT_MESSAGES };
[33]142                while(<MESSAGES>) {
143                    next if /^\s*#/ || /^\s*$/;
144                    chomp;
145                    my($key,$value) = split ' ', $_, 2;
146                    ($options{messages}{$key} = $value) =~ s/\s+$//;
147                }
148                close MESSAGES;
149            } else {
[53]150                carp '[' . (caller(0))[3] . "] Could not read messages file $options{messages}: $!";
[33]151            }
[1]152        }
[34]153    } else {
154        $options{messages} = { %DEFAULT_MESSAGES };
[1]155    }
156   
[53]157    # character set
[34]158    my $charset = $options{charset};
159   
[33]160    # save the build options so they can be used from write_module
161    $self->{build_options} = { %options };
162   
163    # remove our custom options before we hand off to CGI::FormBuilder
[34]164    delete $options{$_} foreach qw(form_only css extra_css charset);
[33]165   
[21]166    # expand groups
[70]167    if (my %groups = %{ $self->{form_spec}{groups} || {} }) {       
[42]168        for my $section (@{ $self->{form_spec}{sections} || [] }) {
169            foreach (grep { $$_[0] eq 'group' } @{ $$section{lines} }) {
170                $$_[1]{group} =~ s/^\%//;       # strip leading % from group var name
171               
172                if (exists $groups{$$_[1]{group}}) {
173                    my @fields; # fields in the group
174                    push @fields, { %$_ } foreach @{ $groups{$$_[1]{group}} };
175                    for my $field (@fields) {
176                        $$field{label} ||= ucfirst $$field{name};
177                        $$field{name} = "$$_[1]{name}_$$field{name}";               
178                    }
179                    $_ = [ 'group', { label => $$_[1]{label} || ucfirst(join(' ',split('_',$$_[1]{name}))), group => \@fields } ];
[29]180                }
[21]181            }
182        }
183    }
[1]184   
[33]185    # the actual fields that are given to CGI::FormBuilder
[42]186    # make copies so that when we trim down the sections
187    # we don't lose the form field information
[21]188    $self->{form_spec}{fields} = [];
[29]189   
190    for my $section (@{ $self->{form_spec}{sections} || [] }) {
191        for my $line (@{ $$section{lines} }) {
192            if ($$line[0] eq 'group') {
[42]193                push @{ $self->{form_spec}{fields} }, { %{$_} } foreach @{ $$line[1]{group} };
[29]194            } elsif ($$line[0] eq 'field') {
[42]195                push @{ $self->{form_spec}{fields} }, { %{$$line[1]} };
[29]196            }
[21]197        }
198    }
199   
[39]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}}) {
[33]207                $$_{validate} = $patterns{$$_{validate}};
[39]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}}) {
[53]211                warn '[' . (caller(0))[3] . "] validate coderefs don't work yet";
[39]212                delete $$_{validate};
[42]213##                 $$_{validate} = $subs{$$_{validate}};
[33]214            }
215        }
216    }
217   
[50]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   
[1]223    # substitute in list names
[50]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};
[1]238        }
[50]239    } continue {
240        delete $$_{list};
[30]241    }
[21]242   
[30]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   
[63]250    # use columns for displaying checkbox fields larger than 2 items
[14]251    foreach (@{ $self->{form_spec}{fields} }) {
[60]252        if (ref $$_{options} and @{ $$_{options} } >= 3) {
253            $$_{columns} = int(@{ $$_{options} } / 8) + 1;
254        }
[14]255    }
[1]256   
[24]257    # remove extraneous undefined values
[64]258    # also check for approriate version of CGI::FormBuilder
259    # for some advanced options
260    my $FB_version = CGI::FormBuilder->VERSION;
[24]261    for my $field (@{ $self->{form_spec}{fields} }) {
262        defined $$field{$_} or delete $$field{$_} foreach keys %{ $field };
[64]263       
[66]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                }
[64]270            }
271        }
[68]272    }
[24]273   
[64]274    # assign the field names to the sections
[39]275    foreach (@{ $self->{form_spec}{sections} }) {
[42]276        for my $line (@{ $$_{lines} }) {
277            if ($$line[0] eq 'field') {
278                $$line[1] = $$line[1]{name};
279            }
[39]280        }
281    }
282   
[69]283    # gather together all of the form options
284    $self->{form_options} = {
[23]285        %DEFAULT_OPTIONS,
[33]286        # need to explicity set the fields so that simple text fields get picked up
[29]287        fields   => [ map { $$_{name} } @{ $self->{form_spec}{fields} } ],
[24]288        required => [ map { $$_{name} } grep { $$_{required} } @{ $self->{form_spec}{fields} } ],
289        title => $self->{form_spec}{title},
[25]290        text  => $self->{form_spec}{description},
[1]291        template => {
292            type => 'Text',
293            engine => {
294                TYPE       => 'STRING',
[34]295                SOURCE     => $form_only ? $self->_form_template : $self->_template($css, $charset),
[11]296                DELIMITERS => [ qw(<% %>) ],
[1]297            },
298            data => {
[29]299                sections    => $self->{form_spec}{sections},
[14]300                author      => $self->{form_spec}{author},
301                description => $self->{form_spec}{description},
[1]302            },
303        },
304        %options,
[69]305    };
306   
307    # create the form object
308    $self->{form} = CGI::FormBuilder->new(%{ $self->{form_options} });
309   
310    # ...and set up its fields
[1]311    $self->{form}->field(%{ $_ }) foreach @{ $self->{form_spec}{fields} };
312   
[16]313    # mark structures as built
314    $self->{built} = 1;
315   
[1]316    return $self;
317}
318
319sub write {
320    my ($self, $outfile) = @_;
[16]321   
322    # automatically call build if needed to
323    # allow the new->parse->write shortcut
324    $self->build unless $self->{built};
325   
[1]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
[69]335# dump the form options as eval-able code
336sub _form_options_code {
[39]337    my $self = shift;
[69]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')
[70]344# TODO: revise this code to use the new 'fieldopts'
345# option to the FB constructor (requires FB 3.02)
[69]346sub _field_setup_code {
347    my $self = shift;
348    my $object_name = shift || '$form';
349    return join(
[12]350        "\n", 
[69]351        map { $object_name . '->field' . Data::Dumper->Dump([$_],['*field']) . ';' } @{ $self->{form_spec}{fields} }
[12]352    );
[69]353}   
[39]354
355sub write_module {
356    my ($self, $package, $use_tidy) = @_;
357
[53]358    croak '[' . (caller(0))[3] . '] Expecting a package name' unless $package;
[39]359   
[52]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   
[69]364    my $form_options = $self->_form_options_code;
365    my $field_setup = $self->_field_setup_code('$self');
[39]366   
[69]367    # old style of module
368    # TODO: how to keep this (as deprecated method)
369    my $old_module = <<END;
[12]370package $package;
371use strict;
372use warnings;
373
374use CGI::FormBuilder;
375
[15]376sub get_form {
[39]377    my \$q = shift;
378
[69]379    my \$self = CGI::FormBuilder->new(
380        $form_options,
381        \@_,
382    );
[12]383   
[69]384    $field_setup
385   
386    return \$self;
[12]387}
388
389# module return
3901;
391END
[23]392
[69]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
[42]419    _write_output_file($module, (split(/::/, $package))[-1] . '.pm', $use_tidy);
[39]420    return $self;
421}
422
423sub write_script {
424    my ($self, $script_name, $use_tidy) = @_;
425
[53]426    croak '[' . (caller(0))[3] . '] Expecting a script name' unless $script_name;
[39]427   
[69]428    my $form_options = $self->_form_options_code;
429    my $field_setup = $self->_field_setup_code('$form');
430
[39]431    my $script = <<END;
432#!/usr/bin/perl
433use strict;
434use warnings;
435
436use CGI::FormBuilder;
437
[69]438my \$form = CGI::FormBuilder->new(
439    $form_options
440);
[39]441
[69]442$field_setup
[39]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) = @_;
[16]457    if ($use_tidy) {
458        # clean up the generated code, if asked
459        eval 'use Perl::Tidy';
[52]460        unless ($@) {
461            Perl::Tidy::perltidy(source => \$source_code, destination => $outfile, argv => $TIDY_OPTIONS);
462        } else {
[53]463            carp '[' . (caller(0))[3] . "] Can't tidy the code: $@" if $@;
[52]464            # fallback to just writing it as-is
465            open OUT, "> $outfile" or die $!;
466            print OUT $source_code;
467            close OUT;
468        }
[16]469    } else {
470        # otherwise, just print as is
[39]471        open OUT, "> $outfile" or die $!;
472        print OUT $source_code;
473        close OUT;
[12]474    }
475}
476
[39]477
[16]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};
[1]484
[16]485    return $self->{form};
486}
487
[12]488sub _form_template {
[33]489    my $self = shift;
[34]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>] : '' %>
[12]494<% $start %>
[23]495<%
496    # drop in the hidden fields here
497    $OUT = join("\n", map { $$_{field} } grep { $$_{type} eq 'hidden' } @fields);
[34]498%>} .
499q[
[29]500<%
501    SECTION: while (my $section = shift @sections) {
[67]502        $OUT .= qq[<fieldset>\n];
503        $OUT .= qq[  <legend>$$section{head}</legend>] if $$section{head};
[30]504        $OUT .= qq[<table id="] . ($$section{id} || '_default') . qq[">\n];
[67]505        #$OUT .= qq[  <caption><h2 class="sectionhead">$$section{head}</h2></caption>] if $$section{head};
[29]506        TABLE_LINE: for my $line (@{ $$section{lines} }) {
507            if ($$line[0] eq 'head') {
[71]508                $OUT .= qq[  <tr><th class="subhead" colspan="2"><h2>$$line[1]</h2></th></tr>\n]
[50]509            } elsif ($$line[0] eq 'note') {
510                $OUT .= qq[  <tr><td class="note" colspan="2">$$line[1]</td></tr>\n]
[29]511            } elsif ($$line[0] eq 'field') {
[42]512                local $_ = $field{$$line[1]};
[34]513               
[29]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>];
[30]518               
519                # special case single value checkboxes
520                if ($$_{type} eq 'checkbox' && @{ $$_{options} } == 1) {
521                    $OUT .= qq[<th></th>];
522                } else {
[66]523                    $OUT .= '<th class="label">' . ($$_{required} ? qq[<strong class="required">$$_{label}</strong>] : "$$_{label}") . '</th>';
[30]524                }
[33]525               
526                # mark invalid fields
[29]527                if ($$_{invalid}) {
[34]528                    $OUT .= "<td>$$_{field} $$_{comment} ] . $msg_invalid . q[</td>";
[29]529                } else {
[30]530                    $OUT .= qq[<td>$$_{field} $$_{comment}</td>];
[29]531                }
[33]532               
[30]533                $OUT .= qq[</tr>\n];
534               
[29]535            } elsif ($$line[0] eq 'group') {
[42]536                my @group_fields = map { $field{$_} } map { $$_{name} } @{ $$line[1]{group} };
[29]537                $OUT .= (grep { $$_{invalid} } @group_fields) ? qq[  <tr class="invalid">\n] : qq[  <tr>\n];
538               
539                $OUT .= '    <th class="label">';
[70]540                $OUT .= (grep { $$_{required} } @group_fields) ? qq[<strong class="required">$$line[1]{label}</strong>] : "$$line[1]{label}";
[29]541                $OUT .= qq[</th>\n];
542               
[59]543                $OUT .= qq[    <td><span class="fieldgroup">];
[29]544                $OUT .= join(' ', map { qq[<small class="sublabel">$$_{label}</small> $$_{field} $$_{comment}] } @group_fields);
[63]545                #TODO: allow comments on field groups
546                $OUT .= " ] . $msg_invalid . q[" if grep { $$_{invalid} } @group_fields;
[59]547               
548                $OUT .= qq[    </span></td>\n];
[29]549                $OUT .= qq[  </tr>\n];
550            }   
[21]551        }
[29]552        # close the table if there are sections remaining
553        # but leave the last one open for the submit button
[67]554        if (@sections) {
555            $OUT .= qq[</table>\n];
556            $OUT .= qq[</fieldset>\n];
557        }
[29]558    }
559%>
[12]560  <tr><th></th><td style="padding-top: 1em;"><% $submit %></td></tr>
561</table>
[67]562</fieldset>
[12]563<% $end %>
564];
565}
566
[34]567# usage: $self->_pre_template($css, $charset)
[33]568sub _pre_template {
[12]569    my $self = shift;
[30]570    my $css = shift || $DEFAULT_CSS;
[34]571    my $charset = shift || $DEFAULT_CHARSET;
572    my $msg_author = 'sprintf("' . quotemeta($self->{build_options}{messages}{text_author}) . '", $author)';
[33]573    return 
[12]574q[<html>
[1]575<head>
[34]576  <meta http-equiv="Content-Type" content="text/html; charset=] . $charset . q[" />
[1]577  <title><% $title %><% $author ? ' - ' . ucfirst $author : '' %></title>
[33]578  <style type="text/css">
[42]579] . $css . q[  </style>
[33]580  <% $jshead %>
[1]581</head>
582<body>
583
584<h1><% $title %></h1>
[34]585<% $author ? qq[<p id="author">] . ] . $msg_author . q{ . q[</p>] : '' %>
586};
[33]587}
588
589sub _post_template {
[34]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 />
[1]595<div id="footer">
[34]596  <p id="creator"><% $msg_madewith %></p>
[1]597</div>
598</body>
599</html>
600];
601}
602
[42]603# usage: $self->_template($css, $charset)
[33]604sub _template {
605    my $self = shift;
[34]606    return $self->_pre_template(@_) . $self->_form_template . $self->_post_template;
[33]607}
608
[7]609sub dump { 
610    eval "use YAML;";
611    unless ($@) {
612        print YAML::Dump(shift->{form_spec});
613    } else {
[53]614        warn '[' . (caller(0))[3] . "] Can't dump form spec structure using YAML: $@";
[7]615    }
616}
[1]617
618
619# module return
6201;
621
622=head1 NAME
623
[21]624Text::FormBuilder - Create CGI::FormBuilder objects from simple text descriptions
[1]625
626=head1 SYNOPSIS
627
[16]628    use Text::FormBuilder;
629   
[1]630    my $parser = Text::FormBuilder->new;
631    $parser->parse($src_file);
632   
[16]633    # returns a new CGI::FormBuilder object with
634    # the fields from the input form spec
[7]635    my $form = $parser->form;
[19]636   
637    # write a My::Form module to Form.pm
638    $parser->write_module('My::Form');
[1]639
[23]640=head1 REQUIRES
641
[56]642L<Parse::RecDescent>,
643L<CGI::FormBuilder>,
644L<Text::Template>,
645L<Class::Base>
[23]646
[1]647=head1 DESCRIPTION
648
[38]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
[46]671=head1 METHODS
672
[1]673=head2 new
674
[38]675    my $parser = Text::FormBuilder->new;
676
[1]677=head2 parse
678
[42]679    # parse a file (regular scalar)
[19]680    $parser->parse($filename);
[7]681   
[19]682    # or pass a scalar ref for parse a literal string
683    $parser->parse(\$string);
[42]684   
685    # or an array ref to parse lines
686    $parser->parse(\@lines);
[19]687
[42]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.
[19]691
692=head2 parse_file
693
694    $parser->parse_file($src_file);
695   
[7]696    # or as a class method
[16]697    my $parser = Text::FormBuilder->parse($src_file);
[7]698
699=head2 parse_text
700
[16]701    $parser->parse_text($src);
702
[19]703Parse the given C<$src> text. Returns the parser object.
[16]704
[42]705=head2 parse_array
706
707    $parser->parse_array(@lines);
708
709Concatenates and parses C<@lines>. Returns the parser object.
710
[1]711=head2 build
712
[12]713    $parser->build(%options);
[7]714
[12]715Builds the CGI::FormBuilder object. Options directly used by C<build> are:
716
717=over
718
[19]719=item C<form_only>
[12]720
721Only uses the form portion of the template, and omits the surrounding html,
[19]722title, author, and the standard footer. This does, however, include the
723description as specified with the C<!description> directive.
[12]724
[30]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
[38]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
[72]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
[34]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
[12]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
[16]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
[7]788=head2 form
789
790    my $form = $parser->form;
791
[16]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.
[7]795
[1]796=head2 write
797
[7]798    $parser->write($out_file);
799    # or just print to STDOUT
800    $parser->write;
801
[29]802Calls C<render> on the FormBuilder form, and either writes the resulting
803HTML to a file, or to STDOUT if no filename is given.
[7]804
[12]805=head2 write_module
806
[70]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
[16]810    $parser->write_module($package, $use_tidy);
[12]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
[16]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.
[12]818
[16]819First, you parse the formspec and write the module, which you can do as a one-liner:
820
[19]821    $ perl -MText::FormBuilder -e"Text::FormBuilder->parse('formspec.txt')->write_module('My::Form')"
[16]822
[72]823B<FIXME> And then, in your CGI script, use the new module:
[16]824
[12]825    #!/usr/bin/perl -w
826    use strict;
827   
828    use CGI;
[19]829    use My::Form;
[12]830   
831    my $q = CGI->new;
[19]832    my $form = My::Form::get_form($q);
[12]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
[16]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
[19]845    # write tidier code
846    $parser->write_module('My::Form', 1);
847
[39]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
[7]886=head2 dump
887
[16]888Uses L<YAML> to print out a human-readable representation of the parsed
[7]889form spec.
890
[46]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
[33]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
[1]928=head1 LANGUAGE
929
[19]930    field_name[size]|descriptive label[hint]:type=default{option1[display string],...}//validate
[1]931   
932    !title ...
933   
[12]934    !author ...
935   
[16]936    !description {
937        ...
938    }
939   
[42]940    !pattern NAME /regular expression/
[16]941   
[42]942    !list NAME {
[7]943        option1[display string],
944        option2[display string],
[1]945        ...
946    }
[12]947   
[42]948    !group NAME {
949        field1
950        field2
951        ...
952    }
953   
[29]954    !section id heading
955   
[12]956    !head ...
[50]957   
958    !note {
959        ...
960    }
[1]961
962=head2 Directives
963
964=over
965
966=item C<!pattern>
967
[12]968Defines a validation pattern.
969
[1]970=item C<!list>
971
[12]972Defines a list for use in a C<radio>, C<checkbox>, or C<select> field.
973
[42]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
[1]983=item C<!title>
984
[50]985Title of the form.
986
[7]987=item C<!author>
988
[50]989Author of the form.
990
[16]991=item C<!description>
992
[19]993A brief description of the form. Suitable for special instructions on how to
994fill out the form.
995
[29]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
[12]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
[50]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
[1]1012=back
1013
1014=head2 Fields
1015
[24]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:
[1]1020
[24]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
[71]1033Quoted strings are also how you can set the label for a field to be blank:
1034
1035    unlabeled_field|''
1036
[34]1037Now, back to the beginning. Form fields are each described on a single line.
[24]1038The simplest field is just a name (which cannot contain any whitespace):
1039
[19]1040    color
1041
1042This yields a form with one text input field of the default size named `color'.
[34]1043The generated label for this field would be ``Color''. To add a longer or more\
1044descriptive label, use:
[19]1045
1046    color|Favorite color
1047
[34]1048The descriptive label can be a multiword string, as described above. So if you
1049want punctuation in the label, you should single quote it:
[19]1050
[34]1051    color|'Fav. color'
1052
[19]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
[23]1061    password
1062    file
1063    checkbox
1064    radio
[19]1065    select
[23]1066    hidden
[19]1067    static
1068
[21]1069To change the size of the input field, add a bracketed subscript after the
1070field name (but before the descriptive label):
[19]1071
[21]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
[56]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.
[61]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
[64]1089C<file> fields, and you must have L<CGI::FormBuilder> 3.02 or higher.
1090Growable fields also require JavaScript to function correctly.
[61]1091
1092    # you can have as many people as you like
1093    person*:text
1094
[21]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
[24]1100Values are in a comma-separated list of single words or multiword strings
1101inside curly braces. Whitespace between values is irrelevant.
[21]1102
[26]1103To add more descriptive display text to a value in a list, add a square-bracketed
[19]1104``subscript,'' as in:
1105
1106    ...:select{red[Scarlet],blue[Azure],green[Olive Drab]}
1107
[1]1108If you have a list of options that is too long to fit comfortably on one line,
[26]1109you should use the C<!list> directive:
[1]1110
[19]1111    !list MONTHS {
1112        1[January],
1113        2[February],
1114        3[March],
1115        # and so on...
1116    }
1117   
1118    month:select@MONTHS
1119
[26]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
[30]1126In this case, the label ``I want to recieve more information'' will be
1127printed to the right of the checkbox.
[26]1128
[19]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
[24]1134Default values can also be either single words or multiword strings.
1135
[19]1136To validate a field, include a validation type at the end of the field line:
1137
1138    email|Email address//EMAIL
1139
[21]1140Valid validation types include any of the builtin defaults from L<CGI::FormBuilder>,
[19]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
[24]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
[42]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
[63]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
[1]1188=head2 Comments
1189
1190    # comment ...
1191
1192Any line beginning with a C<#> is considered a comment.
1193
[7]1194=head1 TODO
1195
[66]1196=head2 Documentation/Tests
[52]1197
[64]1198Document use of the parser as a standalone module
1199
[72]1200Make sure that the docs match the generated code.
1201
[66]1202Better tests!
1203
1204=head2 Language/Parser
1205
[42]1206Allow renaming of the submit button; allow renaming and inclusion of a
1207reset button
1208
[63]1209Allow comments on group fields (rendered after the all the fields)
[52]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
[66]1214C<!include> directive to include external formspec files
1215
1216=head2 Code generation/Templates
1217
[70]1218Revise the generated form constructing code to use the C<fieldopts>
1219option to C<< FB->new >>; will require FB 3.02 to run.
1220
[66]1221Better integration with L<CGI::FormBuilder>'s templating system
1222
[31]1223Allow for custom wrappers around the C<form_template>
1224
[30]1225Maybe use HTML::Template instead of Text::Template for the built in template
[28]1226(since CGI::FormBuilder users may be more likely to already have HTML::Template)
1227
[23]1228=head1 BUGS
1229
[42]1230Creating two $parsers in the same script causes the second one to get the data
1231from the first one.
[26]1232
[42]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
[1]1237=head1 SEE ALSO
1238
[50]1239L<http://textformbuilder.berlios.de>
[1]1240
[50]1241L<CGI::FormBuilder>, L<http://formbuilder.org>
1242
[23]1243=head1 THANKS
1244
[26]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
[29]1247sections.
[23]1248
[61]1249And of course, to Nathan Wiger, for giving use CGI::FormBuilder in the
1250first place. Thanks Nate!
1251
[16]1252=head1 AUTHOR
1253
[34]1254Peter Eichman C<< <peichman@cpan.org> >>
[16]1255
1256=head1 COPYRIGHT AND LICENSE
1257
[53]1258Copyright E<copy>2004-2005 by Peter Eichman.
[16]1259
1260This program is free software; you can redistribute it and/or
1261modify it under the same terms as Perl itself.
1262
[1]1263=cut
Note: See TracBrowser for help on using the repository browser.