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 |