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 |