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