lib/CGI/FormBuilder/Multi.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 56 | 80 | 70.0 |
branch | 16 | 40 | 40.0 |
condition | 3 | 11 | 27.2 |
subroutine | 9 | 10 | 90.0 |
pod | 5 | 5 | 100.0 |
total | 89 | 146 | 60.9 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | |||||||
2 | ########################################################################### | ||||||
3 | # Copyright (c) Nate Wiger http://nateware.com. All Rights Reserved. | ||||||
4 | # Please visit http://formbuilder.org for tutorials, support, and examples. | ||||||
5 | ########################################################################### | ||||||
6 | |||||||
7 | package CGI::FormBuilder::Multi; | ||||||
8 | |||||||
9 | =head1 NAME | ||||||
10 | |||||||
11 | CGI::FormBuilder::Multi - Create multi-page FormBuilder forms | ||||||
12 | |||||||
13 | =head1 SYNOPSIS | ||||||
14 | |||||||
15 | use CGI::FormBuilder::Multi; | ||||||
16 | use CGI::Session; # or something similar | ||||||
17 | |||||||
18 | # Top-level "meta-form" | ||||||
19 | my $multi = CGI::FormBuilder::Multi->new( | ||||||
20 | |||||||
21 | # form 1 options | ||||||
22 | { fields => [qw(name email daytime_phone evening_phone)], | ||||||
23 | title => 'Basic Info', | ||||||
24 | template => 'page1.tmpl', | ||||||
25 | validate => { name => 'NAME', email => 'EMAIL' }, | ||||||
26 | required => [qw(name email daytime_phone)], | ||||||
27 | }, | ||||||
28 | |||||||
29 | # form 2 options | ||||||
30 | { fields => [qw(billing_name billing_card billing_exp | ||||||
31 | billing_address billing_city billing_state | ||||||
32 | billing_zip billing_phone)], | ||||||
33 | title => 'Billing', | ||||||
34 | template => 'page2.tmpl', | ||||||
35 | required => 'ALL', | ||||||
36 | }, | ||||||
37 | |||||||
38 | # form 3 options | ||||||
39 | { fields => [qw(same_as_billing shipping_address | ||||||
40 | shipping_city shipping_state shipping_zip)], | ||||||
41 | title => 'Shipping', | ||||||
42 | template => 'page3.tmpl', | ||||||
43 | required => 'ALL', | ||||||
44 | }, | ||||||
45 | |||||||
46 | # a couple options specific to this module | ||||||
47 | navbar => 1, | ||||||
48 | |||||||
49 | # remaining options (not in hashrefs) apply to all forms | ||||||
50 | header => 1, | ||||||
51 | method => 'POST', | ||||||
52 | submit => 'Continue', | ||||||
53 | values => $dbi_hashref_query, | ||||||
54 | ); | ||||||
55 | |||||||
56 | # Get current page's form | ||||||
57 | my $form = $multi->form; | ||||||
58 | |||||||
59 | if ($form->submitted && $form->validate) { | ||||||
60 | |||||||
61 | # Retrieve session id | ||||||
62 | my $sid = $form->sessionid; | ||||||
63 | |||||||
64 | # Initialize session | ||||||
65 | my $session = CGI::Session->new("driver:File", $sid, {Directory=>'/tmp'}); | ||||||
66 | |||||||
67 | # Automatically store updated data in session | ||||||
68 | $session->save_param($form); | ||||||
69 | |||||||
70 | # last page? | ||||||
71 | if ($multi->page == $multi->pages) { | ||||||
72 | print $form->confirm; | ||||||
73 | exit; | ||||||
74 | } | ||||||
75 | |||||||
76 | # Still here, goto next page | ||||||
77 | $multi->page++; | ||||||
78 | |||||||
79 | # And re-get form (no "my" on $form!) | ||||||
80 | $form = $multi->form; | ||||||
81 | |||||||
82 | # Make sure it has the right sessionid | ||||||
83 | $form->sessionid($session->id); | ||||||
84 | |||||||
85 | # on page 3 we have special field handling | ||||||
86 | if ($multi->page == 3) { | ||||||
87 | $form->field(name => 'same_as_billing', | ||||||
88 | type => 'checkbox', | ||||||
89 | options => 'Yes', | ||||||
90 | jsclick => 'this.form.submit()'); | ||||||
91 | } | ||||||
92 | } | ||||||
93 | |||||||
94 | # Fall through and print next page's form | ||||||
95 | print $form->render; | ||||||
96 | |||||||
97 | =cut | ||||||
98 | |||||||
99 | 1 | 1 | 533 | use strict; | |||
1 | 2 | ||||||
1 | 24 | ||||||
100 | 1 | 1 | 3 | use warnings; | |||
1 | 1 | ||||||
1 | 24 | ||||||
101 | 1 | 1 | 3 | no warnings 'uninitialized'; | |||
1 | 1 | ||||||
1 | 30 | ||||||
102 | |||||||
103 | 1 | 1 | 5 | use CGI::FormBuilder; | |||
1 | 1 | ||||||
1 | 24 | ||||||
104 | 1 | 1 | 5 | use CGI::FormBuilder::Util; | |||
1 | 1 | ||||||
1 | 755 | ||||||
105 | |||||||
106 | our $VERSION = '3.10'; | ||||||
107 | |||||||
108 | our %DEFAULT = ( | ||||||
109 | pagename => '_page', | ||||||
110 | navbar => 0, | ||||||
111 | ); | ||||||
112 | |||||||
113 | sub new { | ||||||
114 | 1 | 1 | 1 | 53 | my $mod = shift; | ||
115 | 1 | 33 | 5 | my $class = ref($mod) || $mod; | |||
116 | |||||||
117 | # Arg parsing is a little more complex than FormBuilder proper, | ||||||
118 | # since we keep going thru our options until we don't see hashrefs | ||||||
119 | 1 | 2 | my @forms = (); | ||||
120 | 1 | 3 | while (ref $_[0]) { | ||||
121 | 3 | 5 | push @forms, shift; | ||||
122 | } | ||||||
123 | |||||||
124 | # Remaining options are form opts | ||||||
125 | 1 | 6 | my %opt = arghash(@_); | ||||
126 | |||||||
127 | # If no forms, and specified number of pages, use that instead | ||||||
128 | 1 | 50 | 3 | if ($opt{pages}) { | |||
129 | 0 | 0 | 0 | puke "Can't specify pages and form hashrefs" if @forms; | |||
130 | 0 | 0 | my $p = 0; | ||||
131 | 0 | 0 | push @forms, {} while $p++ < $opt{pages}; | ||||
132 | } | ||||||
133 | 1 | 50 | 3 | puke "Must specify at least one form or 'pages' option for ::Multi" unless @forms; | |||
134 | |||||||
135 | # Check for CGI params | ||||||
136 | # This is duplicated code straight out of FormBuilder.pm, | ||||||
137 | # but it's needed here as well so we can get our _page | ||||||
138 | 1 | 50 | 33 | 4 | unless ($opt{params} && ref $opt{params} ne 'HASH') { | ||
139 | 1 | 756 | require CGI; | ||||
140 | 1 | 21190 | $CGI::USE_PARAM_SEMICOLONS = 0; # fuck ; in urls | ||||
141 | 1 | 10 | $opt{params} = CGI->new($opt{params}); | ||||
142 | } | ||||||
143 | |||||||
144 | # Options for me | ||||||
145 | 1 | 556 | my %me; | ||||
146 | 1 | 10 | while (my($k,$v) = each %DEFAULT) { | ||||
147 | 2 | 100 | 14 | $me{$k} = exists $opt{$k} ? delete $opt{$k} : $v; | |||
148 | } | ||||||
149 | 1 | 3 | $me{forms} = \@forms; | ||||
150 | |||||||
151 | # Plop in our defaults per-form unless it's an object | ||||||
152 | 1 | 50 | 3 | @forms = map { ref $_ eq 'HASH' ? { %opt, %$_ } : $_ } @forms; | |||
3 | 33 | ||||||
153 | |||||||
154 | # Top-level multi | ||||||
155 | 1 | 10 | my $self = bless \%me, $class; | ||||
156 | |||||||
157 | # Copy CGI object into self, and get page | ||||||
158 | 1 | 7 | $self->{params} = $opt{params}; | ||||
159 | 1 | 3 | $self->{keepextras} = $opt{keepextras}; | ||||
160 | 1 | 50 | 5 | $self->{page} = $self->{params}->param($self->{pagename}) || 1; | |||
161 | |||||||
162 | 1 | 33 | return $self; | ||||
163 | } | ||||||
164 | |||||||
165 | # return an lvalue to allow $multi->page++ and $multi->page--; | ||||||
166 | sub page : lvalue { | ||||||
167 | 22 | 22 | 1 | 27 | my $self = shift; | ||
168 | 22 | 100 | 77 | $self->{page} = shift if @_; # rvalue | |||
169 | 22 | 75 | $self->{page}; # lvalue | ||||
170 | } | ||||||
171 | |||||||
172 | *forms = \&pages; | ||||||
173 | sub pages { | ||||||
174 | 4 | 4 | 1 | 167 | my $self = shift; | ||
175 | 4 | 50 | 10 | puke "No arguments allowed to \$multi->pages or \$multi->forms" if @_; | |||
176 | 4 | 5 | return @{$self->{forms}}; | ||||
4 | 12 | ||||||
177 | } | ||||||
178 | |||||||
179 | # return the form from this page, as a new object | ||||||
180 | sub form { | ||||||
181 | 9 | 9 | 1 | 19 | my $self = shift; | ||
182 | 9 | 50 | 19 | puke "No arguments allowed to \$multi->form" if @_; | |||
183 | 9 | 18 | my $page = $self->page; | ||||
184 | 9 | 11 | my $idx = $page - 1; | ||||
185 | |||||||
186 | 9 | 100 | 35 | return $self->{_cache}{forms}[$idx] if $self->{_cache}{forms}[$idx]; | |||
187 | puke "Invalid page $page, no form present" | ||||||
188 | 4 | 100 | 14 | unless my $form = $self->{forms}[$idx]; | |||
189 | |||||||
190 | 3 | 50 | 7 | if (ref $form eq 'CGI::FormBuilder') { | |||
191 | # already constructed | ||||||
192 | } else { | ||||||
193 | 3 | 23 | $form = CGI::FormBuilder->new(%$form); | ||||
194 | } | ||||||
195 | |||||||
196 | # hooks | ||||||
197 | 3 | 7 | $form->page($self->page); | ||||
198 | 3 | 50 | 6 | $form->text(scalar $self->navbar) if $self->{navbar}; # cheat | |||
199 | |||||||
200 | # create new $form and cache for re-get | ||||||
201 | 3 | 8 | $self->{_cache}{forms}[$idx] = $form; | ||||
202 | } | ||||||
203 | |||||||
204 | # allow jumps between pages | ||||||
205 | sub navbar { | ||||||
206 | 0 | 0 | 1 | my $self = shift; | |||
207 | 0 | 0 | $self->{navbar} = shift if @_; | ||||
208 | 0 | my $base = basename; | |||||
209 | 0 | my $pnam = $self->{pagename}; | |||||
210 | 0 | 0 | return '' unless $self->pages > 1; | ||||
211 | |||||||
212 | # Look for extra params to keep | ||||||
213 | # Algorithm here is a bit different | ||||||
214 | 0 | my @keep; | |||||
215 | 0 | 0 | if ($self->{keepextras}) { | ||||
216 | 0 | 0 | unless (ref $self->{keepextras}) { | ||||
217 | 0 | $self->{keepextras} = [ $self->{params}->param ]; | |||||
218 | } | ||||||
219 | 0 | for my $k (@{$self->{keepextras}}) { | |||||
0 | |||||||
220 | 0 | 0 | next if $k eq $pnam; | ||||
221 | 0 | for my $v ($self->{params}->param($k)) { | |||||
222 | 0 | push @keep, { name => $k, value => $v }; | |||||
223 | } | ||||||
224 | } | ||||||
225 | } | ||||||
226 | |||||||
227 | 0 | my @html = (); | |||||
228 | 0 | for (my $p=1; $p <= $self->pages; $p++) { | |||||
229 | 0 | 0 | my $cl = $self->page == $p ? 'fb_multi_page' : 'fb_multi_link'; | ||||
230 | |||||||
231 | # this looks like gibberish | ||||||
232 | my $purl = basename . '?' . join '&', | ||||||
233 | 0 | map { "$_->{name}=$_->{value}" } @keep, | |||||
0 | |||||||
234 | { name => $pnam, value => $p }; | ||||||
235 | |||||||
236 | push @html, htmltag('a', href => $purl, class => $cl) | ||||||
237 | 0 | 0 | . ($self->{forms}[$p-1]{title} || "Page $p") . ''; | ||||
238 | } | ||||||
239 | |||||||
240 | 0 | 0 | return wantarray ? @html : ' '. join(' | ', @html) . ' '; |
||||
241 | } | ||||||
242 | |||||||
243 | 1; | ||||||
244 | __END__ |