########################################################################### # Copyright (c) Nate Wiger http://nateware.com. All Rights Reserved. # Please visit http://formbuilder.org for tutorials, support, and examples. ########################################################################### package CGI::FormBuilder::Multi; =head1 NAME CGI::FormBuilder::Multi - Create multi-page FormBuilder forms =head1 SYNOPSIS use CGI::FormBuilder::Multi; use CGI::Session; # or something similar # Top-level "meta-form" my $multi = CGI::FormBuilder::Multi->new( # form 1 options { fields => [qw(name email daytime_phone evening_phone)], title => 'Basic Info', template => 'page1.tmpl', validate => { name => 'NAME', email => 'EMAIL' }, required => [qw(name email daytime_phone)], }, # form 2 options { fields => [qw(billing_name billing_card billing_exp billing_address billing_city billing_state billing_zip billing_phone)], title => 'Billing', template => 'page2.tmpl', required => 'ALL', }, # form 3 options { fields => [qw(same_as_billing shipping_address shipping_city shipping_state shipping_zip)], title => 'Shipping', template => 'page3.tmpl', required => 'ALL', }, # a couple options specific to this module navbar => 1, # remaining options (not in hashrefs) apply to all forms header => 1, method => 'POST', submit => 'Continue', values => $dbi_hashref_query, ); # Get current page's form my $form = $multi->form; if ($form->submitted && $form->validate) { # Retrieve session id my $sid = $form->sessionid; # Initialize session my $session = CGI::Session->new("driver:File", $sid, {Directory=>'/tmp'}); # Automatically store updated data in session $session->save_param($form); # last page? if ($multi->page == $multi->pages) { print $form->confirm; exit; } # Still here, goto next page $multi->page++; # And re-get form (no "my" on $form!) $form = $multi->form; # Make sure it has the right sessionid $form->sessionid($session->id); # on page 3 we have special field handling if ($multi->page == 3) { $form->field(name => 'same_as_billing', type => 'checkbox', options => 'Yes', jsclick => 'this.form.submit()'); } } # Fall through and print next page's form print $form->render; =cut use strict; use warnings; no warnings 'uninitialized'; use CGI::FormBuilder; use CGI::FormBuilder::Util; our $VERSION = '3.10'; our %DEFAULT = ( pagename => '_page', navbar => 0, ); sub new { my $mod = shift; my $class = ref($mod) || $mod; # Arg parsing is a little more complex than FormBuilder proper, # since we keep going thru our options until we don't see hashrefs my @forms = (); while (ref $_[0]) { push @forms, shift; } # Remaining options are form opts my %opt = arghash(@_); # If no forms, and specified number of pages, use that instead if ($opt{pages}) { puke "Can't specify pages and form hashrefs" if @forms; my $p = 0; push @forms, {} while $p++ < $opt{pages}; } puke "Must specify at least one form or 'pages' option for ::Multi" unless @forms; # Check for CGI params # This is duplicated code straight out of FormBuilder.pm, # but it's needed here as well so we can get our _page unless ($opt{params} && ref $opt{params} ne 'HASH') { require CGI; $CGI::USE_PARAM_SEMICOLONS = 0; # fuck ; in urls $opt{params} = CGI->new($opt{params}); } # Options for me my %me; while (my($k,$v) = each %DEFAULT) { $me{$k} = exists $opt{$k} ? delete $opt{$k} : $v; } $me{forms} = \@forms; # Plop in our defaults per-form unless it's an object @forms = map { ref $_ eq 'HASH' ? { %opt, %$_ } : $_ } @forms; # Top-level multi my $self = bless \%me, $class; # Copy CGI object into self, and get page $self->{params} = $opt{params}; $self->{keepextras} = $opt{keepextras}; $self->{page} = $self->{params}->param($self->{pagename}) || 1; return $self; } # return an lvalue to allow $multi->page++ and $multi->page--; sub page : lvalue { my $self = shift; $self->{page} = shift if @_; # rvalue $self->{page}; # lvalue } *forms = \&pages; sub pages { my $self = shift; puke "No arguments allowed to \$multi->pages or \$multi->forms" if @_; return @{$self->{forms}}; } # return the form from this page, as a new object sub form { my $self = shift; puke "No arguments allowed to \$multi->form" if @_; my $page = $self->page; my $idx = $page - 1; return $self->{_cache}{forms}[$idx] if $self->{_cache}{forms}[$idx]; puke "Invalid page $page, no form present" unless my $form = $self->{forms}[$idx]; if (ref $form eq 'CGI::FormBuilder') { # already constructed } else { $form = CGI::FormBuilder->new(%$form); } # hooks $form->page($self->page); $form->text(scalar $self->navbar) if $self->{navbar}; # cheat # create new $form and cache for re-get $self->{_cache}{forms}[$idx] = $form; } # allow jumps between pages sub navbar { my $self = shift; $self->{navbar} = shift if @_; my $base = basename; my $pnam = $self->{pagename}; return '' unless $self->pages > 1; # Look for extra params to keep # Algorithm here is a bit different my @keep; if ($self->{keepextras}) { unless (ref $self->{keepextras}) { $self->{keepextras} = [ $self->{params}->param ]; } for my $k (@{$self->{keepextras}}) { next if $k eq $pnam; for my $v ($self->{params}->param($k)) { push @keep, { name => $k, value => $v }; } } } my @html = (); for (my $p=1; $p <= $self->pages; $p++) { my $cl = $self->page == $p ? 'fb_multi_page' : 'fb_multi_link'; # this looks like gibberish my $purl = basename . '?' . join '&', map { "$_->{name}=$_->{value}" } @keep, { name => $pnam, value => $p }; push @html, htmltag('a', href => $purl, class => $cl) . ($self->{forms}[$p-1]{title} || "Page $p") . ''; } return wantarray ? @html : '
'. join(' | ', @html) . '
';
}
1;
__END__
=head1 DESCRIPTION
This module works with C