CGI-Application-Plugin-FormState-0.12/0042755000175200001440000000000010336541255016772 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/t/0042755000175200001440000000000010336541255017235 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/t/tmpl/0042755000175200001440000000000010336541255020211 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/t/tmpl/basic.html0100644000175200001440000000005010336541255022146 0ustar michaeluserscap_form_state:CGI-Application-Plugin-FormState-0.12/t/tmpl/some_storage_name.html0100644000175200001440000000005610336541255024562 0ustar michaeluserssome_storage_name:CGI-Application-Plugin-FormState-0.12/t/01-basic.t0100644000175200001440000001027710336541255020723 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Name = 'cap_form_state'; my $Storage_Hash; { package WebApp1; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, '[webapp1] new session initialized'); } sub start { my $self = shift; my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (1)'); # Store some parameters $self->form_state->param('name' => 'Road Runner'); is($self->form_state->param('name'), 'Road Runner', '[webapp1] form_state: name'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name']), '[webapp2] form_state keys (2)'); $self->form_state->clear_params; is($self->form_state->param('name'), undef, '[webapp1] form_state: name (cleared)'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (3)'); $self->form_state->param('name' => 'Bugs Bunny'); $self->form_state->param('occupation' => 'Having Fun'); # Store some other parameters via hashref $self->form_state->param({ 'name2' => 'Wile E. Coyote', 'occupation' => 'Cartoon Character', }); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys (4)'); is($self->form_state->param('name'), 'Bugs Bunny', '[webapp1] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp1] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp1] form_state: occupation'); my $t = $self->load_tmpl('t/tmpl/basic.html'); my $output = $t->output; $Storage_Hash = $self->form_state->id; my $session_key = 'form_state_cap_form_state_' . $self->form_state->id; is($session_key, $self->form_state->session_key, 'session key'); is($self->form_state->name, 'cap_form_state', 'name'); is($output, "$Storage_Name:$Storage_Hash", 'form_state_id added to output'); } } { package WebApp2; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, '[webapp2] previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; # Retrieve some parameters my @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys'); # Retrieve some parameters is($self->form_state->param('name'), 'Bugs Bunny', '[webapp2] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp2] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp2] form_state: occupation'); } } WebApp1->new->run; my $query = CGI->new; $query->param($Storage_Name, $Storage_Hash); WebApp2->new(QUERY => $query)->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/t/pod.t0100644000175200001440000000021410336541255020174 0ustar michaelusers#!perl -T use Test::More; eval "use Test::Pod 1.14"; plan skip_all => "Test::Pod 1.14 required for testing POD" if $@; all_pod_files_ok(); CGI-Application-Plugin-FormState-0.12/t/20-old-style-basic.t0100644000175200001440000001303610336541255022632 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Name = 'some_storage_name'; my $Other_Storage_Name = 'other_storage_name'; my $Storage_Hash; { package WebApp1; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, '[webapp1] new session initialized'); $self->form_state; } sub start { my $self = shift; eval { $self->form_state->init($Storage_Name, 'bongo' => 'bubba'); }; ok($@, "prevented from calling init with bad option"); my $exists = $self->form_state->init($Storage_Name); ok(!$exists, '[webapp1] form state did not already exist'); my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (1)'); # Store some parameters $self->form_state->param('name' => 'Road Runner'); is($self->form_state->param('name'), 'Road Runner', '[webapp1] form_state: name'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name']), '[webapp2] form_state keys (2)'); $self->form_state->clear_params; is($self->form_state->param('name'), undef, '[webapp1] form_state: name (cleared)'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (3)'); $self->form_state->param('name' => 'Bugs Bunny'); $self->form_state->param('occupation' => 'Having Fun'); # Store some other parameters via hashref $self->form_state->param({ 'name2' => 'Wile E. Coyote', 'occupation' => 'Cartoon Character', }); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys (4)'); is($self->form_state->param('name'), 'Bugs Bunny', '[webapp1] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp1] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp1] form_state: occupation'); my $t = $self->load_tmpl('t/tmpl/some_storage_name.html'); my $output = $t->output; $Storage_Hash = $self->form_state->id; is($output, "$Storage_Name:$Storage_Hash", 'form_state_id added to output'); } } { package WebApp2; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, '[webapp2] previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; my $exists = $self->form_state->init($Storage_Name); ok($exists, '[webapp2] form state already existed'); # Retrieve some parameters my @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys'); # Retrieve some parameters is($self->form_state->param('name'), 'Bugs Bunny', '[webapp2] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp2] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp2] form_state: occupation'); } } { package WebApp3; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, '[webapp3] previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; my $exists = $self->form_state->init($Other_Storage_Name); ok(!$exists, '[webapp3] form state did not already exist'); my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp3] form_state keys empty'); } } WebApp1->new->run; my $query = CGI->new; $query->param($Storage_Name, $Storage_Hash); WebApp2->new(QUERY => $query)->run; $query = CGI->new; $query->param($Other_Storage_Name, $Storage_Hash); WebApp3->new(QUERY => $query)->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/t/03-storage_name.t0100644000175200001440000001520310336541255022302 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Name = 'some_storage_name'; my $Other_Storage_Name = 'other_storage_name'; my $Storage_Hash; { package WebApp1; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, '[webapp1] new session initialized'); } sub start { my $self = shift; eval { $self->form_state->config('name' => $Storage_Name, 'bongo' => 'bubba'); }; ok($@, "prevented from calling config with bad option"); $self->form_state->config(name => $Storage_Name); my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (1)'); # Store some parameters $self->form_state->param('name' => 'Road Runner'); is($self->form_state->param('name'), 'Road Runner', '[webapp1] form_state: name'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name']), '[webapp2] form_state keys (2)'); $self->form_state->clear_params; is($self->form_state->param('name'), undef, '[webapp1] form_state: name (cleared)'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (3)'); $self->form_state->param('name' => 'Bugs Bunny'); $self->form_state->param('occupation' => 'Having Fun'); # Store some other parameters via hashref $self->form_state->param({ 'name2' => 'Wile E. Coyote', 'occupation' => 'Cartoon Character', }); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys (4)'); is($self->form_state->param('name'), 'Bugs Bunny', '[webapp1] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp1] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp1] form_state: occupation'); my $t = $self->load_tmpl('t/tmpl/some_storage_name.html'); my $output = $t->output; $Storage_Hash = $self->form_state->id; my $session_key = 'form_state_' . $Storage_Name . '_' . $self->form_state->id; is($session_key, $self->form_state->session_key, 'session key'); is($self->form_state->name, $Storage_Name, 'name'); is($output, "$Storage_Name:$Storage_Hash", 'form_state_id added to output'); # Test that we can get back to the original storage name without recreating the storage $self->form_state->config(name => 'bogus'); $self->form_state->config(name => $Storage_Name); is($self->form_state->param('name'), 'Bugs Bunny', '[webapp1 (2)] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp1 (2)] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp1 (2)] form_state: occupation'); } } { package WebApp2; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, '[webapp2] previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; my $exists = $self->form_state->config('name' => $Storage_Name); ok($exists, '[webapp2] form state already existed'); # Retrieve some parameters my @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys'); # Retrieve some parameters is($self->form_state->param('name'), 'Bugs Bunny', '[webapp2] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp2] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp2] form_state: occupation'); } } { package WebApp3; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, '[webapp3] previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; my $exists = $self->form_state->config('name' => $Other_Storage_Name); ok(!$exists, '[webapp3] form state did not already exist'); my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp3] form_state keys empty'); } } WebApp1->new->run; my $query = CGI->new; $query->param($Storage_Name, $Storage_Hash); WebApp2->new(QUERY => $query)->run; $query = CGI->new; $query->param($Other_Storage_Name, $Storage_Hash); WebApp3->new(QUERY => $query)->run; # Re-run webapp 2 but passing the storage hash via the query string, instead of # via a posted parameter. $ENV{'REQUEST_METHOD'} = 'POST'; $ENV{'SERVER_PORT'} = '80'; $ENV{'SCRIPT_NAME'} = '/cgi-bin/app.cgi'; $ENV{'SERVER_NAME'} = 'www.example.com'; $ENV{'PATH_INFO'} = '/my/happy/pathy/info'; $ENV{'QUERY_STRING'} = "$Storage_Name=$Storage_Hash"; $query = CGI->new; WebApp2->new(QUERY => $query)->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/t/04-delete.t0100644000175200001440000000631510336541255021105 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Name = 'cap_form_state'; my $Storage_Hash; { package WebApp1; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, '[webapp1] new session initialized'); } sub start { my $self = shift; $self->form_state->delete; is($self->session->param('foo'), 42, '[webapp1] other session data still there'); my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (1)'); # Store some parameters $self->form_state->param('name' => 'Road Runner'); is($self->form_state->param('name'), 'Road Runner', '[webapp1] form_state: name'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name']), '[webapp2] form_state keys (2)'); $self->form_state->clear_params; is($self->form_state->param('name'), undef, '[webapp1] form_state: name (cleared)'); @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (3)'); $self->form_state->param('name' => 'Bugs Bunny'); $self->form_state->param('occupation' => 'Having Fun'); # Store some other parameters via hashref $self->form_state->param({ 'name2' => 'Wile E. Coyote', 'occupation' => 'Cartoon Character', }); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'name2', 'occupation']), '[webapp2] form_state keys (4)'); is($self->form_state->param('name'), 'Bugs Bunny', '[webapp1] form_state: name'); is($self->form_state->param('name2'), 'Wile E. Coyote', '[webapp1] form_state: name2'); is($self->form_state->param('occupation'), 'Cartoon Character', '[webapp1] form_state: occupation'); my $t = $self->load_tmpl('t/tmpl/basic.html'); my $output = $t->output; $Storage_Hash = $self->form_state->id; my $session_key = 'form_state_cap_form_state_' . $self->form_state->id; is($session_key, $self->form_state->session_key, 'session key'); is($self->form_state->name, 'cap_form_state', 'name'); is($output, "$Storage_Name:$Storage_Hash", 'form_state_id added to output'); $self->form_state->delete; is($self->session->param('foo'), 42, '[webapp1] other session data still there'); ok(!exists $self->session->dataref->{$session_key}, 'storage has been removed'); } } WebApp1->new->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/t/21-old-style-expires.t0100644000175200001440000000543210336541255023232 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Name = 'some_storage_name'; my $Storage_Hash; { package WebApp1; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, 'new session initialized'); $self->form_state; } sub start { my $self = shift; $self->form_state->init($Storage_Name, 'expires' => '1s'); my $session_key = 'form_state_' . $Storage_Name . '_' . $self->form_state->id; my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (1)'); # Store some parameters $self->form_state->param( 'name' => 'Road Runner', 'occupation' => 'Having Fun', ); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'occupation']), '[webapp2] form_state keys (2)'); is($self->form_state->param('name'), 'Road Runner', '[webapp1] form_state: name'); is($self->form_state->param('occupation'), 'Having Fun', '[webapp1] form_state: occupation'); } } { package WebApp2; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, 'previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; $self->form_state->init($Storage_Name); # Retrieve some parameters my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state is empty'); } } WebApp1->new->run; sleep 2; my $query = CGI->new; $query->param($Storage_Name, $Storage_Hash); WebApp2->new(QUERY => $query)->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/t/02-expires.t0100644000175200001440000000544110336541255021317 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Hash; { package WebApp1; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, 'new session initialized'); } sub start { my $self = shift; $self->form_state->config('expires' => '1s'); my $session_key = 'form_state_cap_form_state_' . $self->form_state->id; is($session_key, $self->form_state->session_key, 'session key'); is($self->form_state->name, 'cap_form_state', 'name'); my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state keys (1)'); # Store some parameters $self->form_state->param( 'name' => 'Road Runner', 'occupation' => 'Having Fun', ); @keys = sort $self->form_state->param; ok(eq_array(\@keys, ['name', 'occupation']), '[webapp2] form_state keys (2)'); is($self->form_state->param('name'), 'Road Runner', '[webapp1] form_state: name'); is($self->form_state->param('occupation'), 'Having Fun', '[webapp1] form_state: occupation'); } } { package WebApp2; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); # init session and verify that it's got content $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", $Session_ID, { Directory => 't' } ], ); is($self->session->param('foo'), 42, 'previous session initialized'); $self->start_mode('start'); $self->run_modes([qw/start/]); } sub start { my $self = shift; # Retrieve some parameters my @keys = sort $self->form_state->param; ok(eq_array(\@keys, []), '[webapp2] form_state is empty'); } } WebApp1->new->run; sleep 2; my $query = CGI->new; $query->param('cap_form_state', $Storage_Hash); WebApp2->new(QUERY => $query)->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/t/00.load.t0100644000175200001440000000040110336541255020545 0ustar michaelusersuse Test::More tests => 1; use CGI::Application; BEGIN { @::ISA = qw(CGI::Application) }; BEGIN { use_ok( 'CGI::Application::Plugin::FormState' ); } diag( "Testing CGI::Application::Plugin::FormState $CGI::Application::Plugin::FormState::VERSION" ); CGI-Application-Plugin-FormState-0.12/t/pod-coverage.t0100644000175200001440000000103110336541255021763 0ustar michaelusers#!perl -T use Test::More; eval "use Test::Pod::Coverage 1.04"; if ($@) { plan skip_all => "Test::Pod::Coverage 1.04 required for testing POD coverage"; } else { plan tests => 1; } # The 'form_state' sub is not documented separately. Instead it's documented in # the calling syntax of every other sub. pod_coverage_ok( "CGI::Application::Plugin::FormState", { also_private => [ qr/^(form_state)|(init)$/ ], }, "CAP::FormState, POD coverage, but marking the 'form_state' and 'init' subs as private", ); CGI-Application-Plugin-FormState-0.12/t/05-auto-config.t0100644000175200001440000000312510336541255022053 0ustar michaelusers use strict; use Test::More 'no_plan'; $ENV{'CGI_APP_RETURN_ONLY'} = 1; use CGI::Session; use CGI::Session::Driver::file; use File::Spec; $CGI::Session::Driver::file::FileName = 'session.dat'; my $Session_ID; my $Storage_Name = 'cap_form_state'; my $Storage_Hash; { package BaseWebApp; use CGI::Application; use vars qw(@ISA); BEGIN { @ISA = qw(CGI::Application); } use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use Test::More; sub setup { my $self = shift; $self->run_modes(['start']); $self->session_config( CGI_SESSION_OPTIONS => [ "driver:File", undef, { Directory => 't' } ], ); $Session_ID = $self->session->id; $self->session->param('foo', 42); is($self->session->param('foo'), 42, "[" . (ref $self) ."] new session initialized"); } } { package WebApp1; @WebApp1::ISA = ('BaseWebApp'); use Test::More; sub start { my $self = shift; ok($self->form_state->id, 'id autoconfigs'); } } { package WebApp2; @WebApp2::ISA = ('BaseWebApp'); use Test::More; sub start { my $self = shift; ok($self->form_state->name, 'name autoconfigs'); } } { package WebApp3; @WebApp3::ISA = ('BaseWebApp'); use Test::More; sub start { my $self = shift; ok($self->form_state->session_key, 'session_key autoconfigs'); } } WebApp1->new->run; WebApp2->new->run; WebApp3->new->run; unlink File::Spec->catfile('t', $CGI::Session::Driver::file::FileName); CGI-Application-Plugin-FormState-0.12/lib/0042755000175200001440000000000010336541255017540 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/lib/CGI/0042755000175200001440000000000010336541255020142 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/lib/CGI/Application/0042755000175200001440000000000010336541255022405 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/lib/CGI/Application/Plugin/0042755000175200001440000000000010336541255023643 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/lib/CGI/Application/Plugin/FormState.pm0100644000175200001440000004047210336541255026107 0ustar michaelusers package CGI::Application::Plugin::FormState; use warnings; use strict; use CGI::Application; use CGI::Session::ID::md5; use CGI::Application::Plugin::Session; use vars qw(@ISA @EXPORT); use Carp; use Scalar::Util qw(weaken isweak); use Exporter; @ISA = qw(Exporter); @EXPORT = qw(form_state); our $CGIAPP_Namespace = '__CAP_FORM_STATE'; my $Default_Expires = '2d'; my $Default_Storage_Name = 'cap_form_state'; sub import { my $caller = scalar(caller); if ($caller->can('add_callback')) { $caller->add_callback('load_tmpl', \&_add_form_state_id_to_tmpl); } else { croak "CAP::FormState: Calling package ($caller) is not a CGI::Application module so cannot install load_tmpl hooks. If you are using \@ISA instead of 'use base', make sure it is in a BEGIN { } block, and make sure these statements appear before the plugin is loaded"; } goto &Exporter::import; } =head1 NAME CGI::Application::Plugin::FormState - Store Form State without Hidden Fields =head1 VERSION Version 0.12 =cut our $VERSION = '0.12'; =head1 SYNOPSIS FormState is just a temporary stash that you can use for storing and retrieving private parameters in your multi-page form. use CGI::Application::Plugin::FormState; my $form = < ... EOF sub form_display_runmode { my $self = shift; # Store some parameters $self->form_state->param('name' => 'Road Runner'); $self->form_state->param('occupation' => 'Having Fun'); my $t = $self->load_tmpl(scalarref => \$form); return $t->output; } sub form_process_runmode { my $self = shift; # Retrieve some parameters print $self->form_state->param('name'); # 'Road Runner' print $self->form_state->param('occupation'); # 'Having Fun' } =head1 EXAMPLE This is a more complete example, using L. use CGI::Application::Plugin::Session; use CGI::Application::Plugin::FormState; use CGI::Application::Plugin::ValidateRM; my $form = < ... EOF sub my_form_display { my $self = shift; my $errs = shift; my $t = $self->load_tmpl(scalarref => \$form); # Stash some data into it $self->form_state->param('name' => 'Wile E. Coyote'); $self->form_state->param('occupation' => 'Mining Engineer'); # Normal ValidateRM error handling $t->param($errs) if $errs; return $t->output; } sub my_form_process { my $self; # Normal ValidateRM validation my ($results, $err_page) = $self->check_rm('my_form_display','_my_form_profile'); return $err_page if $err_page; # The data from the submitted form my $params = $self->dfv_results; $params->{'name'} = $self->form_state->param('name'); # 'Wile E. Coyote' $params->{'occupation'} = $self->form_state->param('occupation'); # 'Mining Engineer' # Now do something interesting with $params # ... my $t = $self->load_tmpl('success.html'); return $t->output; } # Standard ValiateRM profile sub _my_form_profile { return { required => 'email', msgs => { any_errors => 'some_errors', prefix => 'err_', }, }; } =head1 DESCRIPTION C provides a temporary storage area within the user's session for storing form-related data. The main use of this is for multi-page forms. Instead of using hidden fields to store data related to the form, you store and retrieve values from the form state. In the first instance of your app: $self->form_state->param('some_name' => 'some_value'); $self->form_state->param('some_other_name' => 'some_other_value'); And later, in a different instance of your app: $val1 = $self->form_state->param('some_name'); $val2 = $self->form_state->param('some_other_name'); To connect the first instance and the second, you put a single hidden field in your template: You don't have to worry about creating the template param C; it is added automatically to your template parameters via the C hook. If you want to use a parameter other than C you can do so via the C parameter to Cconfig>. If you're skeptical about whether all this abstraction is a good idea, see L<"MOTIVATION">, below. =head1 PRESERVING FORM STATE ACROSS REDIRECTS You can include the form_state hash in a link: my $link = '/app.cgi?rm=list&cap_form_state=' . $self->form_state->id; If you use L, you can easily create redirect this way: $self->redirect('/app.cgi?rm=list&cap_form_state=' . $self->form_state->id); If you also use L it is as simple as: $self->redirect($self->link('/app.cgi', 'rm' => 'list', 'cap_form_state' => $self->form_state->id)); Or, in the case of a link to the currently running app: $self->redirect($self->self_link('rm' => 'list', 'cap_form_state' => $self->form_state->id)); =head1 IMPLEMENTATION When you call C<< $self->form_state >> for the first time, a top-level key is created in the user's session. This key contains a random, hard-to-guess element. It might look something like: form_state_cap_form_state_84eb13cfed01764d9c401219faa56d53 All data you place in the form state with C is stored in the user's session under this key. You pass the name of this key on to the next instance of your application by means of a hidden field in your form: You manually put this hidden field in your template. The template parameter C is automatically added to your template parameters via the C hook. It contains the random, hard-to-guess portion (e.g. C<84eb13cfed01764d9c401219faa56d53>). When the template is filled, the hidden field will look something like this: Since all values are stored on the server in the user's session, the user can't tamper with any of them. To keep old form_data from cluttering up the user's session, the system uses L's C feature to expire old form state keys after a reasonable amount of time has passed (2 days by default). You can manually delete a form state storage by calling: $self->form_state->delete; =cut sub _new { my ($class, $webapp) = @_; my $self = { '__CGIAPP_OBJ' => $webapp, '__STORAGE_NAME' => undef, '__STORAGE_HASH' => undef, '__STORAGE_KEY' => undef, '__EXPIRES' => undef, '__CONFIGURED' => undef, }; # Force reference to CGI::Application object to be weak to avoid # circular references weaken($self->{'__CGIAPP_OBJ'}); return bless $self, $class; } sub form_state { my ($self) = @_; # It's possible that in the future we will allow named configs, e.g. # $self->form_state('foo')->param('bar); if (not exists $self->{$CGIAPP_Namespace}->{'__DEFAULT_CONFIG'}) { $self->{$CGIAPP_Namespace}->{'__DEFAULT_CONFIG'} = __PACKAGE__->_new($self); } return $self->{$CGIAPP_Namespace}->{'__DEFAULT_CONFIG'}; } =head1 METHODS =over 4 =item config(%options) Sets defaults for the plugin. B $self->form_state->config('name' => 'storage_names', 'expires' => '3d') The following options are allowed: =over 4 =item name Sets the name of the default form state storage. This name is used for the key in the user's session, for the name of hidden form field, and the template parameter used to fill the hidden form field. So if you set the C to C: $self->form_state_config('name' => 'foo'); then the hidden field in your template should look like this: and the key in the user's session would look something like this: form_state_foo_84eb13cfed01764d9c401219faa56d53 =item expires Indicates when form state storage keys should expire and disappear from the user's session. Uses the same format as L's C. Defaults to 2 days (C<'2d'>). To cancel expiration and make the form state last as long as the user's session does, use: $self->form_state_config('expires' => 0); =back =cut my %Installed_Callback; sub config { my $self = shift; my %args = @_; my $storage_name = delete $args{'name'} if exists $args{'name'}; my $expires = delete $args{'expires'} if exists $args{'expires'}; if (keys %args) { croak "CAP::FormState: unknown configuration keys: " . (join ', ', keys %args); } $self->{'__STORAGE_NAME'} ||= $storage_name || $Default_Storage_Name; $self->{'__EXPIRES'} ||= $expires || $Default_Expires; $self->{'__CONFIGURED'} = 1; $self->_initialize; } # Deprecated method. Wrapper around 'config' sub init { my $self = shift; my $storage_name = shift; my %args = @_; $self->config('name' => $storage_name, %args); } sub _initialize { my $self = shift; my $webapp = $self->{__CGIAPP_OBJ}; my $expires = $self->{__EXPIRES}; my $storage_name = $self->{__STORAGE_NAME}; my $storage_hash = $webapp->query->param($storage_name) || $webapp->query->url_param($storage_name); my $storage_key; my $already_exists; if ($storage_hash) { # restore existing session $storage_key = 'form_state_' . $storage_name . '_' . $storage_hash; if (ref $webapp->session->param($storage_key) eq 'HASH') { $already_exists = 1; } } unless ($already_exists) { # create new session $storage_hash = CGI::Session::ID::md5->generate_id; $storage_key = 'form_state_' . $storage_name . '_' . $storage_hash; $webapp->session->param($storage_key => {}); $webapp->query->param($storage_name => $storage_hash); } # reset expiry date on key $webapp->session->expire($storage_key, $expires); $self->{'__STORAGE_NAME'} = $storage_name; $self->{'__STORAGE_HASH'} = $storage_hash; $self->{'__STORAGE_KEY'} = $storage_key; $self->{'__EXPIRES'} = $expires; return 1 if $already_exists; return; } =item param Read and set values in the form state storage. It acts like the C method typically does in modules such as L, L, L, C etc. # set a value $self->form_state->param('some_name' => 'some_value'); # retrieve a value my $val = $self->form_state->param('some_name'); # set multiple values $self->form_state->param( 'some_name' => 'some_value', 'some_other_name' => 'some_other_value', ); # retrive the names of all the keys my @keys = $self->form_state->param; =cut sub param { my $self = shift; if (!$self->{'__CONFIGURED'}) { $self->config; } my $storage_key = $self->{'__STORAGE_KEY'}; my $webapp = $self->{__CGIAPP_OBJ}; my $store = $webapp->session->param($storage_key); if (@_) { my $param; if (ref $_[0] eq 'HASH') { $param = shift; } elsif (@_ == 1) { return $store->{$_[0]}; } else { $param = { @_ }; } $store->{$_} = $param->{$_} for keys %$param; # A param has been changed, so we touch the session $webapp->session->param($storage_key => $store); } else { return keys %$store; } } =item clear_params Clear all of the values in the form state storage: $self->form_state->param('name' => 'Road Runner'); $self->form_state->clear_params; print $self->form_state->param('name'); # undef =cut sub clear_params { my $self = shift; if (!$self->{'__CONFIGURED'}) { $self->config; } my $storage_key = $self->{'__STORAGE_KEY'}; my $webapp = $self->{__CGIAPP_OBJ}; $webapp->session->param($storage_key => {}); } =item delete Deletes the form_state storage from the user's session. =cut sub delete { my $self = shift; # No need to call $self->config and create the session key since we're # just going to delete it. # # However, it's very important not to call session->clear without a key; # if we do, we'll delete all params in the user's session! if ($self->{'__STORAGE_KEY'}) { $self->{__CGIAPP_OBJ}->session->clear($self->{'__STORAGE_KEY'}); } } =item id Returns the current value of the storage param - the "hard to guess" portion of the session key. my $id = $self->form_state->id; =cut sub id { my $self = shift; if (!$self->{'__CONFIGURED'}) { $self->config; } $self->{'__STORAGE_HASH'}; } =item name Returns the current name being used for storage. Defaults to C. my $name = $self->form_state->name; =cut sub name { my $self = shift; if (!$self->{'__CONFIGURED'}) { $self->config; } $self->{'__STORAGE_NAME'}; } =item session_key Returns the full key used for storage in the user's session. my $key = $self->form_state->session_key; # Get the full form state hash my $data = $self->session->param($key); The following can be used to debug the form_state data: use Data::Dumper; print STDERR Dumper $self->session->param($self->form_state->session_key); =back =cut sub session_key { my $self = shift; if (!$self->{'__CONFIGURED'}) { $self->config; } $self->{'__STORAGE_KEY'}; } # add the storage id to the template params sub _add_form_state_id_to_tmpl { my ($self, $ht_params, $tmpl_params, $tmpl_file) = @_; my $storage_hash = $self->form_state->id; my $storage_name = $self->form_state->name; $tmpl_params->{$storage_name} = $storage_hash; } =head1 MOTIVATION =head2 Why not just use hidden fields? Hidden fields are not secure. The end user could save a local copy of your form, change the hidden fields and tamper with your app's form state. =head2 Why not just use the user's session? With C the data is associated with a particular instance of a form, not with the user. If the user gives up halfway through your multi-page form, you don't want their session to be cluttered up with the incomplete form state data. If a user opens up your application in two browser windows (both sharing the same user session), each window should have it's own independent form state. For instance, in an email application the user might have one window open for the inbox and another open for the outbox. If you store the value of C<"current_mailbox"> in the user's session, then one of these windows will go to the wrong mailbox. Finally, the user's session probably sticks around longer than the form state should. =head1 AUTHOR Michael Graham, C<< >> =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 ACKNOWLEDGEMENTS Thanks to Richard Dice and Cees Hek for helping me sort out the issues with this approach. The informative error message text used for when this module is loaded before your app actually C<@ISA> C object was stolen from Cees's L module. =head1 COPYRIGHT & LICENSE Copyright 2005 Michael Graham, All Rights Reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1; CGI-Application-Plugin-FormState-0.12/TODO0100644000175200001440000000117610336541255017462 0ustar michaelusers The following options may eventually be added to C if there is demand: =over 4 =item digest_module Which digest module to use to create the checksum portion of the session key. By default this is C, but you could use any other module with the same interface, such as C. =item keyname_generator Normally the form state is stored in the user's session session using a key like: form_state_storage_name_84eb13cfed01764d9c401219faa56d53 If you want to name keys differently, you can provide a subroutine to generate the name. The subroutine is passed the name of the form state storage. =back CGI-Application-Plugin-FormState-0.12/misc/0042755000175200001440000000000010336541255017725 5ustar michaelusersCGI-Application-Plugin-FormState-0.12/misc/dist0100644000175200001440000000017710336541255020613 0ustar michaelusers#!/bin/sh rm MANIFEST misc/makedocs.pl perl ./Build.PL perl ./Build perl ./Build manifest perl ./Build test perl ./Build dist CGI-Application-Plugin-FormState-0.12/misc/module-starter-opts.txt0100644000175200001440000000020610336541255024411 0ustar michaelusers $ module-starter --mb --module=CGI::Application::Plugin::FormState --author="Michael Graham" --email=mag-perl@occamstoothbrush.com CGI-Application-Plugin-FormState-0.12/misc/style.css0100644000175200001440000001136710336541255021602 0ustar michaelusersBODY, .logo { background: white; } BODY { color: black; font-family: arial,sans-serif; margin: 0; padding: 1ex; } TABLE { border-collapse: collapse; border-spacing: 0; border-width: 0; color: inherit; } IMG { border: 0; } FORM { margin: 0; } input { margin: 2px; } .logo { float: left; width: 264px; height: 77px; } .front .logo { float: none; display:block; } .front .searchbox { margin: 2ex auto; text-align: center; } .front .menubar { text-align: center; } .menubar { background: #006699; margin: 1ex 0; padding: 1px; } .menubar A { padding: 0.8ex; font: bold 10pt Arial,Helvetica,sans-serif; } .menubar A:link, .menubar A:visited { color: white; text-decoration: none; } .menubar A:hover { color: #ff6600; text-decoration: underline; } A:link, A:visited { background: transparent; color: #006699; } A[href="#POD_ERRORS"] { background: transparent; color: #FF0000; } TD { margin: 0; padding: 0; } DIV { border-width: 0; } DT { margin-top: 1em; } .credits TD { padding: 0.5ex 2ex; } .huge { font-size: 32pt; } .s { background: #dddddd; color: inherit; } .s TD, .r TD { padding: 0.2ex 1ex; vertical-align: baseline; } TH { background: #bbbbbb; color: inherit; padding: 0.4ex 1ex; text-align: left; } TH A:link, TH A:visited { background: transparent; color: black; } .box { border: 1px solid #006699; margin: 1ex 0; padding: 0; } .distfiles TD { padding: 0 2ex 0 0; vertical-align: baseline; } .manifest TD { padding: 0 1ex; vertical-align: top; } .l1 { font-weight: bold; } .l2 { font-weight: normal; } .t1, .t2, .t3, .t4 { background: #006699; color: white; } .t4 { padding: 0.2ex 0.4ex; } .t1, .t2, .t3 { padding: 0.5ex 1ex; } /* IE does not support .box>.t1 Grrr */ .box .t1, .box .t2, .box .t3 { margin: 0; } .t1 { font-size: 1.4em; font-weight: bold; text-align: center; } .t2 { font-size: 1.0em; font-weight: bold; text-align: left; } .t3 { font-size: 1.0em; font-weight: normal; text-align: left; } /* width: 100%; border: 0.1px solid #FFFFFF; */ /* NN4 hack */ .datecell { text-align: center; width: 17em; } .cell { padding: 0.2ex 1ex; text-align: left; } .label { background: #aaaaaa; color: black; font-weight: bold; padding: 0.2ex 1ex; text-align: right; white-space: nowrap; vertical-align: baseline; } .categories { border-bottom: 3px double #006699; margin-bottom: 1ex; padding-bottom: 1ex; } .categories TABLE { margin: auto; } .categories TD { padding: 0.5ex 1ex; vertical-align: baseline; } .path A { background: transparent; color: #006699; font-weight: bold; } .pages { background: #dddddd; color: #006699; padding: 0.2ex 0.4ex; } .path { background: #dddddd; border-bottom: 1px solid #006699; color: #006699; /* font-size: 1.4em;*/ margin: 1ex 0; padding: 0.5ex 1ex; } .menubar TD { background: #006699; color: white; } .menubar { background: #006699; color: white; margin: 1ex 0; padding: 1px; } .menubar .links { background: transparent; color: white; padding: 0.2ex; text-align: left; } .menubar .searchbar { background: black; color: black; margin: 0px; padding: 2px; text-align: right; } A.m:link, A.m:visited { background: #006699; color: white; font: bold 10pt Arial,Helvetica,sans-serif; text-decoration: none; } A.o:link, A.o:visited { background: #006699; color: #ccffcc; font: bold 10pt Arial,Helvetica,sans-serif; text-decoration: none; } A.o:hover { background: transparent; color: #ff6600; text-decoration: underline; } A.m:hover { background: transparent; color: #ff6600; text-decoration: underline; } table.dlsip { background: #dddddd; border: 0.4ex solid #dddddd; } .pod PRE { background: #eeeeee; border: 1px solid #888888; color: black; padding: 1em; white-space: pre; } .pod H1 { background: transparent; color: #006699; font-size: large; } .pod H2 { background: transparent; color: #006699; font-size: medium; } .pod IMG { vertical-align: top; } .pod .toc A { text-decoration: none; } .pod .toc LI { line-height: 1.2em; list-style-type: none; } .faq DT { font-size: 1.4em; font-weight: bold; } .chmenu { background: black; color: red; font: bold 1.1em Arial,Helvetica,sans-serif; margin: 1ex auto; padding: 0.5ex; } .chmenu TD { padding: 0.2ex 1ex; } .chmenu A:link, .chmenu A:visited { background: transparent; color: white; text-decoration: none; } .chmenu A:hover { background: transparent; color: #ff6600; text-decoration: underline; } .column { padding: 0.5ex 1ex; vertical-align: top; } .datebar { margin: auto; width: 14em; } .date { background: transparent; color: #008000; } CGI-Application-Plugin-FormState-0.12/misc/makedocs.pl0100644000175200001440000000332010336541255022041 0ustar michaelusers#!/usr/bin/perl # This is a Quick and dirty script to generate docs. # # It can do three things: # 1) make HTML docs that look like those on search.cpan.org # 2) make text docs # 3) copy files # # Run this from the main module directory with: # $ misc/makedocs.pl # my $StyleSheet = "misc/style.css"; my %HTML = ( 'CAP-FormState.html' => 'lib/CGI/Application/Plugin/FormState.pm', ); my %TEXT = ( 'README' => 'lib/CGI/Application/Plugin/FormState.pm', ); my %COPY = ( 'changes.txt' => 'Changes', 'readme.txt' => 'README', ); my @Tempfiles = qw( pod2htmd.tmp pod2htmd.x~~ pod2htmi.tmp pod2htmi.x~~ ); use strict; use File::Copy; local $/; foreach my $target (keys %TEXT) { my $source = $TEXT{$target}; system("pod2text $source $target"); } foreach my $target (keys %HTML) { my $source = $HTML{$target}; system("pod2html --css=$StyleSheet $source $target"); open my $fh, $target or die "can't read $target: $!\n"; my $text = <$fh>; close $fh; # Add
...
$text =~ s/(]*>)/$1
/i; $text =~ s/(<\/body>)/<\/div>$1/i; # remove redundant
 sequences (only necessary in old pod2html)
    # $text =~ s/<\/pre>(\s*)
/$1/imsg;

    # remove redundant 
 tags (only necessary in old pod2html)
    # $text =~ s/<\/pre>(\s*)<\/dd>\s*
\s*
/$1/imsg;


    open my $fh, '>', $target or die "can't clobber $target: $!\n";
    print $fh $text;
    close $fh;

    foreach my $tempfile (@Tempfiles) {
        unlink $tempfile;
    }
}

foreach my $target (keys %COPY) {
    my $source = $COPY{$target};
    copy($source, $target);
}

CGI-Application-Plugin-FormState-0.12/README0100644000175200001440000002654610336541255017662 0ustar  michaelusersNAME
    CGI::Application::Plugin::FormState - Store Form State without Hidden
    Fields

VERSION
    Version 0.12

SYNOPSIS
    FormState is just a temporary stash that you can use for storing and
    retrieving private parameters in your multi-page form.

        use CGI::Application::Plugin::FormState;

        my $form = <
           
           
           ...
           
        EOF

        sub form_display_runmode {
            my $self = shift;

            # Store some parameters
            $self->form_state->param('name'       => 'Road Runner');
            $self->form_state->param('occupation' => 'Having Fun');

            my $t = $self->load_tmpl(scalarref => \$form);
            return $t->output;

        }

        sub form_process_runmode {
            my $self = shift;

            # Retrieve some parameters
            print $self->form_state->param('name');       # 'Road Runner'
            print $self->form_state->param('occupation'); # 'Having Fun'
        }

EXAMPLE
    This is a more complete example, using
    CGI::Application::Plugin::ValidateRM.

        use CGI::Application::Plugin::Session;
        use CGI::Application::Plugin::FormState;
        use CGI::Application::Plugin::ValidateRM;

        my $form = <
           
           
           ...
           
        EOF

        sub my_form_display {
            my $self     = shift;
            my $errs     = shift;
            my $t        = $self->load_tmpl(scalarref => \$form);

            # Stash some data into it
            $self->form_state->param('name'       => 'Wile E. Coyote');
            $self->form_state->param('occupation' => 'Mining Engineer');

            # Normal ValidateRM error handling
            $t->param($errs) if $errs;
            return $t->output;
        }

        sub my_form_process {
            my $self;

            # Normal ValidateRM validation
            my ($results, $err_page) = $self->check_rm('my_form_display','_my_form_profile');
            return $err_page if $err_page;

            # The data from the submitted form
            my $params = $self->dfv_results;

            $params->{'name'}       = $self->form_state->param('name');       # 'Wile E. Coyote'
            $params->{'occupation'} = $self->form_state->param('occupation'); # 'Mining Engineer'

            # Now do something interesting with $params
            # ...

            my $t = $self->load_tmpl('success.html');
            return $t->output;
        }

        # Standard ValiateRM profile
        sub _my_form_profile {
            return {
                required => 'email',
                msgs => {
                        any_errors => 'some_errors',
                        prefix => 'err_',
                },
            };
        }

DESCRIPTION
    "CGI::Application::Plugin::FormState" provides a temporary storage area
    within the user's session for storing form-related data.

    The main use of this is for multi-page forms. Instead of using hidden
    fields to store data related to the form, you store and retrieve values
    from the form state.

    In the first instance of your app:

        $self->form_state->param('some_name' => 'some_value');
        $self->form_state->param('some_other_name' => 'some_other_value');

    And later, in a different instance of your app:

        $val1 = $self->form_state->param('some_name');
        $val2 = $self->form_state->param('some_other_name');

    To connect the first instance and the second, you put a single hidden
    field in your template:

        

    You don't have to worry about creating the template param
    "cap_form_state"; it is added automatically to your template parameters
    via the "load_tmpl" hook.

    If you want to use a parameter other than "cap_form_state" you can do so
    via the "name" parameter to "form_state-"config>.

    If you're skeptical about whether all this abstraction is a good idea,
    see "MOTIVATION", below.

PRESERVING FORM STATE ACROSS REDIRECTS
    You can include the form_state hash in a link:

        my $link = '/app.cgi?rm=list&cap_form_state=' . $self->form_state->id;

    If you use CGI::Application::Plugin::Redirect, you can easily create
    redirect this way:

        $self->redirect('/app.cgi?rm=list&cap_form_state=' . $self->form_state->id);

    If you also use CGI::Application::Plugin::LinkIntegrity it is as simple
    as:

        $self->redirect($self->link('/app.cgi', 'rm' => 'list', 'cap_form_state' => $self->form_state->id));

    Or, in the case of a link to the currently running app:

        $self->redirect($self->self_link('rm' => 'list', 'cap_form_state' => $self->form_state->id));

IMPLEMENTATION
    When you call "$self->form_state" for the first time, a top-level key is
    created in the user's session. This key contains a random, hard-to-guess
    element. It might look something like:

       form_state_cap_form_state_84eb13cfed01764d9c401219faa56d53

    All data you place in the form state with "param" is stored in the
    user's session under this key.

    You pass the name of this key on to the next instance of your
    application by means of a hidden field in your form:

        

    You manually put this hidden field in your template. The template
    parameter "cap_form_state" is automatically added to your template
    parameters via the "load_tmpl" hook. It contains the random,
    hard-to-guess portion (e.g. "84eb13cfed01764d9c401219faa56d53"). When
    the template is filled, the hidden field will look something like this:

        

    Since all values are stored on the server in the user's session, the
    user can't tamper with any of them.

    To keep old form_data from cluttering up the user's session, the system
    uses CGI::Session's "expire" feature to expire old form state keys after
    a reasonable amount of time has passed (2 days by default).

    You can manually delete a form state storage by calling:

        $self->form_state->delete;

METHODS
    config(%options)
        Sets defaults for the plugin.

        Calling config is purely optional, since the defaults should be fine
        most purposes.

            $self->form_state->config('name' => 'storage_names', 'expires' => '3d')

        The following options are allowed:

        name
            Sets the name of the default form state storage. This name is
            used for the key in the user's session, for the name of hidden
            form field, and the template parameter used to fill the hidden
            form field. So if you set the "name" to "foo":

                $self->form_state_config('name' => 'foo');

            then the hidden field in your template should look like this:

                

            and the key in the user's session would look something like
            this:

               form_state_foo_84eb13cfed01764d9c401219faa56d53

        expires
            Indicates when form state storage keys should expire and
            disappear from the user's session. Uses the same format as
            CGI::Session's "expire". Defaults to 2 days ('2d'). To cancel
            expiration and make the form state last as long as the user's
            session does, use:

                $self->form_state_config('expires' => 0);

    param
        Read and set values in the form state storage. It acts like the
        "param" method typically does in modules such as CGI,
        CGI::Application, CGI::Session, "HTML::Template" etc.

            # set a value
            $self->form_state->param('some_name' => 'some_value');

            # retrieve a value
            my $val = $self->form_state->param('some_name');

            # set multiple values
            $self->form_state->param(
                'some_name'       => 'some_value',
                'some_other_name' => 'some_other_value',
            );

            # retrive the names of all the keys
            my @keys = $self->form_state->param;

    clear_params
        Clear all of the values in the form state storage:

           $self->form_state->param('name' => 'Road Runner');
           $self->form_state->clear_params;
           print $self->form_state->param('name'); # undef

    delete
        Deletes the form_state storage from the user's session.

    id  Returns the current value of the storage param - the "hard to guess"
        portion of the session key.

            my $id = $self->form_state->id;

    name
        Returns the current name being used for storage. Defaults to
        "cap_form_state".

            my $name = $self->form_state->name;

    session_key
        Returns the full key used for storage in the user's session.

            my $key = $self->form_state->session_key;

            # Get the full form state hash
            my $data = $self->session->param($key);

        The following can be used to debug the form_state data:

            use Data::Dumper;
            print STDERR Dumper $self->session->param($self->form_state->session_key);

MOTIVATION
  Why not just use hidden fields?
    Hidden fields are not secure. The end user could save a local copy of
    your form, change the hidden fields and tamper with your app's form
    state.

  Why not just use the user's session?
    With "CGI::Application::Plugin::FormState" the data is associated with a
    particular instance of a form, not with the user. If the user gives up
    halfway through your multi-page form, you don't want their session to be
    cluttered up with the incomplete form state data.

    If a user opens up your application in two browser windows (both sharing
    the same user session), each window should have it's own independent
    form state.

    For instance, in an email application the user might have one window
    open for the inbox and another open for the outbox. If you store the
    value of "current_mailbox" in the user's session, then one of these
    windows will go to the wrong mailbox.

    Finally, the user's session probably sticks around longer than the form
    state should.

AUTHOR
    Michael Graham, ""

BUGS
    Please report any bugs or feature requests to
    "bug-cgi-application-plugin-formstate@rt.cpan.org", or through the web
    interface at . I will be notified, and then you'll
    automatically be notified of progress on your bug as I make changes.

ACKNOWLEDGEMENTS
    Thanks to Richard Dice and Cees Hek for helping me sort out the issues
    with this approach.

    The informative error message text used for when this module is loaded
    before your app actually @ISA "CGI::Application" object was stolen from
    Cees's CGI::Application::Plugin::TT module.

COPYRIGHT & LICENSE
    Copyright 2005 Michael Graham, All Rights Reserved.

    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl itself.

CGI-Application-Plugin-FormState-0.12/Changes0100644000175200001440000000175010336541255020263 0ustar  michaelusersRevision history for CGI-Application-Plugin-FormState

0.12    Nov 16, 2005
        - added Makefile.PL compatibility to the distribution

0.11    Oct 05, 2005
        - fixed a serious bug where calling 'delete' before calling
          'param' could clear all values in the user's session

        - 'session_key', 'id' and 'name' now all automatically configure
          the form_state if it hasn't been configured yet.

0.10    Oct 03, 2005
        - removed the need to call 'init'
        - default form storage name is now 'cap_form_state'
        - allowed a form_state storage to be populated and queried in
          the same request
        - added CGI::Application version 4.0 as a prerequisite
        - accepted params from url_param as well as from param
        - made CGI version 2.37 or greater a prerequisite (for url_param)
        - added informative error message if hooks cannot be installed
          on module load.


0.01    Sept 24, 2005
        - First released version

CGI-Application-Plugin-FormState-0.12/Build.PL0100644000175200001440000000137110336541255020263 0ustar  michaelusersuse strict;
use warnings;
use Module::Build;

my $builder = Module::Build->new(
    module_name         => 'CGI::Application::Plugin::FormState',
    license             => 'perl',
    dist_author         => 'Michael Graham ',
    dist_version_from   => 'lib/CGI/Application/Plugin/FormState.pm',
    requires => {
        'Test::More'                        => 0,
        'CGI::Session'                      => 4.0,
        'CGI::Application::Plugin::Session' => 0.09,
        'CGI::Application'                  => 4.0,
        'CGI'                               => 2.37,
    },
    add_to_cleanup      => [ 'CGI-Application-Plugin-FormState-*' ],
    create_makefile_pl  => 'traditional',
);

$builder->create_build_script();
CGI-Application-Plugin-FormState-0.12/MANIFEST.SKIP0100644000175200001440000000017710336541255020670 0ustar  michaelusers^_build
^Build$
^blib
~$
\.cvsignore$
\.bak$
CVS
^cover_db
^readme.txt$
^changes.txt$
^[^/]*\.html$
\.gz$
\.tar$
t/session.dat$CGI-Application-Plugin-FormState-0.12/Makefile.PL0100644000175200001440000000120210336541255020732 0ustar  michaelusers# Note: this file was auto-generated by Module::Build::Compat version 0.03
use ExtUtils::MakeMaker;
WriteMakefile
(
          'PL_FILES' => {},
          'INSTALLDIRS' => 'site',
          'NAME' => 'CGI::Application::Plugin::FormState',
          'VERSION_FROM' => 'lib/CGI/Application/Plugin/FormState.pm',
          'PREREQ_PM' => {
                           'Test::More' => 0,
                           'CGI::Application' => '4',
                           'CGI::Session' => '4',
                           'CGI::Application::Plugin::Session' => '0.09',
                           'CGI' => '2.37'
                         }
        )
;
CGI-Application-Plugin-FormState-0.12/META.yml0100644000175200001440000000071310336541255020237 0ustar  michaelusers---
name: CGI-Application-Plugin-FormState
version: 0.12
author:
  - Michael Graham 
abstract: Store Form State without Hidden Fields
license: perl
requires:
  CGI: 2.37
  CGI::Application: 4
  CGI::Application::Plugin::Session: 0.09
  CGI::Session: 4
  Test::More: 0
provides:
  CGI::Application::Plugin::FormState:
    file: lib/CGI/Application/Plugin/FormState.pm
    version: 0.12
generated_by: Module::Build version 0.2611
CGI-Application-Plugin-FormState-0.12/MANIFEST0100644000175200001440000000064310336541255020121 0ustar  michaelusersBuild.PL
Changes
lib/CGI/Application/Plugin/FormState.pm
Makefile.PL
MANIFEST			This list of files
MANIFEST.SKIP
META.yml
misc/dist
misc/makedocs.pl
misc/module-starter-opts.txt
misc/style.css
README
t/00.load.t
t/01-basic.t
t/02-expires.t
t/03-storage_name.t
t/04-delete.t
t/05-auto-config.t
t/20-old-style-basic.t
t/21-old-style-expires.t
t/pod-coverage.t
t/pod.t
t/tmpl/basic.html
t/tmpl/some_storage_name.html
TODO