This tutorial does not cover the installation of CGI::Application, or any of it's growing number of plug-ins, but will show how you can use CGI::Application and HTML::Template to build one of the most common ancillary interactive applications on the Web: a contact form. Also, this tutorial is not meant as a placement for CGI::Application's well-written POD, which should be required reading.
The locations of your files will vary depending on your server, e.g., you could be on a shared host and using something like /usr/home/foobar and /foobar/public_html/. The salient point is that the CGI::Application applications (server-side) are placed "out-of-reach" of the public Web directory (client-side).
Sample Application
CGI::Application, Plugin's, and HTML::Template
index.cgi (our instance script)
Notes:
- this instance script will pass a PARAM to the application
- in this example, we need to output the form that now in a .tmpl file. Though some browsers will actually display the .tmpl file without HTML::Template, we'll use an instance of our application to simply display the form.
- the instance script might be evoked by a text link:
<a href="http://www.acmecorp.com/contact/">Contact Us</a>
- note that paths are relative to the location of the .tmpl file and not the instance script or application.
#!/usr/local/bin/perl -T
use lib "/opt/foobar/myapps/";
use warnings;
use strict;
use Acmecorp::Contact;
my $app = Acmecorp::Contact->new( PARAM => 'client' );
$app->run();
contact.tmpl ('rm' is our run mode and will be passed upon submission to our instance script '/contact/index.cgi')
<form action="/contact/index.cgi" method="post">
<input type="hidden" name="rm" value="s" />
<tmpl_if errors>
<tmpl_loop errors>
<p style="color: red"><tmpl_var error></p>
</tmpl_loop>
</tmpl_if>
<p>Today's date: <tmpl_var today></p>
<p>Name: <input name="name" type="text" value="" /></p>
<p>Address: <input name="address" type="text" value="" /></p>
<p>City: <input name="city" type="text" value="" /></p>
<p>More info: <input name="more_info" type="checkbox" value="yes
+" /></p>
<p><input name="Submit" type="submit" value="Submit" /></p>
</form>
Foobar_Super (a super class for CGI::Application applications)
Notes:
- because you may be creating several CGI::Application applications for other sites on your server, developing a super class eliminate repetition
- CGI::Application Plugin's integrates the modules they are associated with, negating the necessity of loading the native CPAN module
- cgiapp_init is run before anything in the application. In the super class you can read configuration files, set paths, set sessions, etc.
- the PARAM 'client' that was passed by the instance script is used by the super class to 'personalize' the instance.
package Foobar_Super;
use strict;
use warnings;
use base 'CGI::Application';
use CGI::Application::Plugin::FillInForm(qw/fill_form/);
use CGI::Application::Plugin::Config::Simple;
use CGI::Application::Plugin::Redirect;
use CGI::Application::Plugin::Session;
use CGI::Application::Plugin::DBH (qw/dbh_config dbh/);
use HTML::Template;
#--- Start CGI::APP
sub cgiapp_init {
my $self = shift;
#--- Set Paths
$self->config_file( '/opt/foobar/myapps/' . ucfirst $self->param('c
+lient') .'/conf/'. $self->param('client').'.conf');;
$self->tmpl_path( '/var/www/' . $self->param('client') . '/template
+s );
#--- Session
$self->session_config( DEFAULT_EXPIRY => '+8h');
#--- Contact to DB
$self->dbh_config( $self->config_param('db.host'),
$self->config_param('db.user'),
$self->config_param('db.pass'),
{RaiseError => 1} );
}
1;
acmecorp.conf (a configuration file)
Note: read by CA_Super's cgiapp_init
#--- MySQL Server ---
[db]
host = DBI:mysql:foobar:localhost
user = acmecorp
pass = AKCgKYxc
Contact.pm (the actual application called by the instance script
Notes:
- in the method 'display', the CGI::Application convention for specifying a template is used: $self->load_tmpl
- in the first firing of the instance script, no run mode will be passed, so the application will use the default function defined by the $self->start_mod('d') in the setup method. This will simply output, or display, the template file containing our form.
- we also use the CGI::Application::Plugin::FillInForm convention for outputting the form with the fill-in-form values: $self->fill_form
package Acmecorp::Contact;
use base qw(Foobar_Super Common);
use strict;
use warnings;
use MIME::Lite; #load any extra modules needed
use Date::Calc qw(Today);
#--- SETUP Run modes
sub setup {
my $self = shift;
$self->start_mode('d'); #if no run mode, use 'd'
$self->mode_param('rm');
$self->run_modes(
'd' => 'display',
's' => 'save_form'
);
}
#--- Display
sub display {
my $self = shift;
my $template = $self->load_tmpl( 'contact.tmpl',
die_on_bad_params => 0 );
$template->param( today => sprintf( '%4d-%02d-%02d', Today() ) );
+
return $template->output();
}
#--- Process
sub save_form {
my $self = shift;
my ( %sql, @errors, $error, $fifvalues );
($sql{'name'}, $error ) = $self->validate( $self->query->param('nam
+e') );
if ( $error ) { push @errors, ( { 'error' => 'Name'.$error } );
+}
($sql{'address'}, $error ) = $self->validate( $self->query->param('
+address') );
if ( $error ) { push @errors, ( { 'error' => 'Address'.$error }
+); }
($sql{'city'}, $error ) = $self->validate( $self->query->param('cit
+y') );
if ( $error ) { push @errors, ( { 'error' => 'City'.$error } );
+}
$sql{'more_info'} = $self->query->param('more_info');
#if there are errors, return the form with original input and error
+ messages
if ( @errors ) {
my $template = $self->load_tmpl( 'contact.tmpl',
die_on_bad_params => 0,
);
$template->param( errors => \@errors,
today => sprintf( '%4d-%02d-%02d', Today() ),
);
for my $key ( keys %sql ) {
$fifvalues->{$key} = $sql{$key}; #assign fill-in-form values
}
return $self->fill_form( \$template->output, $fifvalues );
} else {
$self->record(\%sql); #record the input
return $self->redirect('/thankyou.html');
}
}
#--- Record
sub record {
my $self = shift;
my $sql = shift;
my %sql = %{ $sql };
#we use CAP::DBH to connect to the DB and execute our SQL statemen
+t
my $stmt = 'INSERT INTO contacts (' . join(',', keys %sql) . ')
VALUES (' . join(',', ('?') x keys %sql) . ')';
$self->dbh->do($stmt, undef, values %sql);
}
1;
Common.pm (a module with common methods)
package Common;
sub validate {
my $self = shift;
my $to_check = shift;
if ( $to_check !~ /^([\w ]+)$/ ) {
return ( $to_check, " has invalid characters or is blank" );
} else {
return $1;
}
}
1;
Summary
This tutorial has shown use of the basis tenents of using CGI::Application as an application framework:
- file layout and directory structure
- instance scripts
- run modes
- cgiapp_init and redirect
- integration of CGI::Application::Plugin's
- use of super class
- intro to the HTML::Template templating system
As always, you are encouraged to read the POD for CGI::Application and then take a look at the growing number of Plugins to see if CGI::Application can further streamline your coding process.
Other Resources
CGI::Application Wiki
Mailing List
HTML::Template Tutorial
Red Antiqua Tutorial
Using CGI::Application