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