Chapter 23

How to Build CGI On the Fly


CONTENTS


Dynamic CGI programming is a method of creating CGI scripts where none existed before. CGI scripts are the core of many dynamic applications on the Web. We've already seen many examples of using CGI script programming for developing applications that generate pages dynamically. We will definitely see more later in the book. This chapter is about a technique for developing applications dynamically. The purpose of generating scripts dynamically is to make the job of the Web developer and Webmaster easier. A lot of times, through the course of experimentation and development, we find that useful tools can be created for in-house use. The popularity of these in-house applications can grow once certain people see the potential for their use in commercial site construction. The author finds it interesting that more often, project managers and technical builders see these new prototype applications as great additions to the sites they are currently working on.

For you, the developer and the Webmaster, the temptation is to give your prototype application up for review and hope to see it being used in real world situations. Then almost immediately, you find that everyone in the company wants to use your application. The traditional Web development strategy has been to collect the source code for your application and write a document on how it works and how to install it. Likely, the people who are responsible for actually installing it will not be as experienced in developing Web applications as you so without very explicit documentation, you find yourself working closely with each team helping them get the application to work in their environment. This is a time sink. You will waste a considerable amount of time customizing the application for each project that needs your new application.

The new technologies emerging now for Web developers greatly aid the administration and management of Web pages-HTML checking, link integrity, graphical interfaces for moving pages from one section of a site to another. These products did not exist before, but they do now.

A spin-off of dynamic Web maintenance and administration is the use of dynamic CGI generation to install custom versions of applications you develop for a wide variety of projects.

Basics of Dynamic CGI Generation

Dynamic CGI generation doesn't use new technology, but it transforms the methods of writing CGI scripts that are reused very frequently.

For example, take the mail-feedback form. Most all sites have some kind of interface to allow the user to make comments about the site and request more information. An HTML form based interface is presented and the user fills in their comments and presses the "send" button. There are numerous CGI scripts out there to deal with this kind of application.

In order to show what dynamic CGI programming is about, lets look at the generic mail-feedback form (see Listing 23.1).


Listing 23.1  feedback.html-A Sample Feedback HTML Form

<html>
<title>Feedback Form</title>

<form method="post" action="/cgi-bin/feedback/feedback.cgi">

Your name: <input name="userName"><BR>
Your Email address: <input name="userEmail"><BR>

Which thing did you like the best about the site:<BR>

The graphics <input name="question Best Part of the Site" type="radio"
value="The graphics">
The text <input name="question Best Part of the Site" type="radio" value="The text">
The XYZ <input name="question Best Part of the Site" type="radio" value="The XYZ">

<p>

Which thing did you like worst about the site:<BR>

The graphics <input name="question Worst Part of the Site" type="radio"
value="The graphics">
The text <input name="question Worst Part of the Site" type="radio" value="The text">
The XYZ <input name="question Worst Part of the Site" type="radio" value="The XYZ">

<p>

Which of the following do you want to see more of:<BR>

Graphics <input type="clickbox" name="question Want more Graphics" value="Yes"><BR>
Text <input type="clickbox" name="question Want more Text" value="Yes"><BR>
Other: <input name="question Want more "><BR>

<p>

Any other comments:<BR>

<textarea name="question User Comments" rows=5 cols=40>
</textarea>

<input type="submit" value="Send Feedback">
</form>

Our feedback form has three parts. First we decide to ask the user which things they liked best about the site. The buttons are mutually exclusive so the user can only select one, only one can be the best. Then, we ask the user to pick which part of the site is the worst. Again, mutually exclusive buttons to pick the very worst part of the site.

Finally, let the user tell you which parts of the site should be expanded. These are not mutually exclusive buttons so the user can pick as many as he wants. Plus, we let them define a new category at the end of the clickbox list. And, then we close with a generic text field for the user to make comments about the site in general.

This type of feedback form doesn't specifically target any kind of site. As is, this feedback form doesn't address enough unique parts of a site to gather useful information. The problem with it is the weak questions. The questions are too passive and generic to really make the user feel like the site really cares about their opinion.

Let's look briefly at the CGI script that deals with the input from the form. The form uses four kinds of inputs: radio buttons, text boxes, clickboxes, and text areas. The CGI script doesn't check for null values, but it needs a non-null e-mail address and user name to help construct the mail header information. The other data have really long names. That is so we can generate a mail message that assigns some meaning to the data. The input variables from the HTML form that we want to relay in the e-mail generated by their form are named with "question." The CGI script (see Listing 23.2) looks for any variable from the form that has a name that begins with "question," strips off that text and
uses the remaining text as the label assigned to the answer given by the user for that question.


Listing 23.2  feedback.cgi-A CGI Script to Handle the feedback.html

#!/usr/local/bin/Perl
require 'Web.pl';
%Form = &getStdin;
&beginHTML;
open(MAIL, "| /usr/lib/sendmail webmaster");
print MAIL "Subject: Web comments from $Form{'userName'}\n",
      "From: $Form{'userEmail'}\n\n";
      "----------------------------\n",
      "Answers to user questionaire:\n";
foreach $answer (sort keys %Form) {
   if ($answer =~ /^question (.*)$/) {
    print MAIL "$1: ",
          "$Form{$answer}\n\n";
   }
close MAIL;

That is missing from this CGI script is any output to the user. User feedback CGI scripts handle the system tasks of sending the comments and user input to the Webmaster, but the script needs to also generate a thank you page. To simplify the example, use the CGI sandwich method of generating the thank you page:

open(THANKS,"< /standard/thankyou.html");
@Lines = <THANKS>;
close(THANKS);
print @Lines;

Dynamic CGI is about creating CGI scripts just as dynamic HTML is created. Dynamic HTML is created from mixing input from a user and the pre-defined "template" of what the resulting page should look like. Dynamic CGI creates scripts perfectly written to handle the data of HTML forms (that too are dynamically created). The pair of HTML form and CGI script are all products of a dynamic CGI system.

Programming languages like C are written in text and processed by software to create executable programs. The software that processes the source code of a programming language like C and creates an executable program is a compiler. A compiler has two parts-a lexical analyzer and a parser. The lexical analyzer scans the source code and finds tokens, units of the program that the parser is looking for. The lexical analyzer passes the tokens to the parser. The parser is written to look for accepted patterns of tokens. As it finds tokens in order, the parser organizes them in a new structure internally. After all of the tokens are found by the lexical analyzer, the parser is already in the process of interpreting its internal "program structure"; the parser uses the program structure to generate a executable program that the computer understands. What a compiler needs is the program source code, a C program for example, (see Fig. 23.1) and the knowledge of what to do with the tokens it finds from scanning the source code.

Figure 23.1: The process of compiling a C program is somewhat analogous to how dynamic CGI works. CGI scripts created on the fly are not compiled, but they are created based on rules (templates) and data from the user.

In the same light, the dynamic CGI system needs two things: An HTML page to ask how the application should run, and a CGI script that can interpret the information from the form and generate a new HTML page and CGI script built to the exact specifications of the dynamic CGI system.

Figure 23.2: The process of generating a CGI script starts from a configuration page.

The dynamic CGI system is like a compiler in that it accepts raw information and with its own internal structure, uses that raw information to build a new kind of CGI-script. The usefulness of dynamic CGI systems, like compilers, is that the same dynamic CGI system can be used repeatedly to create any number of new CGI-based applications. In some ways, all the CGI scripts generated by the same dynamic CGI system are similar, and in other ways they are unique. That is part of the usefulness of dynamic CGI systems-the Webmaster can create versions of commonly used CGI based applications all from using a constant HTML form and "CGI compiler."

You are going to show how to create a dynamic CGI system that will be a "feedback form application builder." First, run through creating a very simple example of dynamic CGI with the help of Nina, our CGI programmer.

Nina is learning about dynamic CGI programming and came up with this example: the greetingTool.

She creates an HTML form that will ask a user's name and then respond by saying "hello" to the user. She doesn't know how fellow developers want to ask "what is your name" and say "hello" for each of the environments the form is used in, so Nina implements a dynamic CGI system that creates a CGI script and HTML form (see Fig. 23.3) for each instance where the Greeting is used.

Figure 23.3: The resulting CGI script and optional HTML file interact in the same way the configuration HTML page and CGI builder script do.

She starts with an HTML form in Listing 23.3.


Listing 23.3  GreetingTool.html-The Greeting Tool Configuration Page

<HTML>
<TITLE>Listing 3, Chapter 23 Programs for Webmaster Expert Solutions</TITLE>
<BODY BGCOLOR=FFFFFF>
<H1>Listing 3, Chapter 23</H1>


This is the configuration form for the GreetingTool, a system for
generating CGI scripts and HTML pages for a custom application.


The GreetingTool creates an HTML document and a CGI script.  The HTML 
document asks the user their name, and the CGI script replies with
a "hello" acknowledgement to the user.

In this configuration tool, you specify how we ask "your name" and how
you respond "hello".

<form method=post action="/cgi-bin/greetingTool.cgi">

How do you want to ask their name:
<input name="askName">
<p>

How do you want to respond with "hello":
<input name="respHello">
<p>

We need a one word name for this new HTML and CGI script.
<p>
(The name "foobar" will be used by default)
<p>
<input name="label" value="foobar">
<p>

<input type="submit" value="Create CGI script">

</form>



<HR>
<A HREF="../index.html">Home</A>
</BODY>
</HTML>

Nina has learned that CGI scripts do two things: generate pages (HTML) and perform system tasks. The system tasks for greetingTool.cgi are special (see Listing 23.4). It is creating a new CGI script and HTML form.


Listing 23.4  greetingTool.cgi-Generates New CGI Scripts Based on Information Collected by the HTML Form

#!/usr/local/bin/perl


require '../lib/web.pl';

%Form = &getStdin;
&beginHTML;

print "<html>\n",
      "<body bgcolor=ffffff>\n",
      "<h1>Chapter 23</h1>\n",
      "<h2>greetingTool</h2>\n",
      "I'm building the HTML document and CGI script...\n",
      "One moment please.<p>\n";

$label = $Form{'label'};

open(HTML,"> $ServerRoot/htdocs/ch23/${label}.html");

print HTML "<html>\n",
           "<title>$label Greeting</title>\n",
           "<body bgcolor=ffffff>\n",
           "<h1>Chapter 23</h1>\n",
           "<h2>greetingTool</h2>\n",
           "<form method=\"post\" action=\"/cgi-bin/${label}.cgi\">\n",
           "$Form{'askName'}:\n",
           "<input name=\"askName\">\n",
           "<p>\n",
           "<input type=\"submit\" value=\"Continue\">\n",
           "</form>\n";

close HTML;

open(CGI, "> $ServerRoot/cgi-bin/${label}.cgi");

print CGI "\#!/usr/local/bin/perl\n",
          "\n",
          "require '../lib/web.pl';\n",
          "\%Form = &getStdin;\n",
          "&beginHTML;\n",
          "\n",
          "print \"<html>\\n\",\n",
          "      \"<title>Greeting</title>\\n\",\n",
          "      \"<body bgcolor=ffffff>\\n\",\n",
          "      \"<h1>Chapter 23</h1>\\n\",\n",
          "      \"<h2>greetingTool</h2>\\n\",\n",
          "      \"$Form{'respHello'} \$Form{'askName'}!\\n\",\n",
          "      \"<p>\\n\",\n",
          "      \"<a href=\\\"/ch23/${label}.html\\\">Back</a>\\n\";\n";
           
          
close CGI;
chmod 0755, "$ServerRoot/cgi-bin/${label}.cgi";


print "Ok... done.  You can access your new HTML page (and result\n",
      "CGI script via this link: ",
      "<a href=\"/ch23/${label}.html\">$label</a>\n";

The process of creating a new Greeting application could be carried out by anyone who can use a Web browser and HTML form. All they need to do is load up the GreetingTool configuration form, enter information on how the resulting Greeting application will look and press "Create Application." No CGI programming experience is necessary to create a Greeting application. Nina can pass her GreetingTool CGI script to other builders and let them install Greeting applications as necessary while she gets back to other work, like converting the feedback form system they have to a dynamic CGI system.

An Application Built with Dynamic CGI Techniques

Our CGI programmer, Nina, from earlier chapters is developing a Web based feedback form for a client's site. She gets a custom feedback form built by one of the HTML builders with all the graphics and layout set up. What Nina is concerned with is taking the input variables from the form and creating a nicely formatted e-mail message sent to the Webmaster of the site containing the users comments. It would take her a few hours of time to build and test a CGI script that collects the data from the HTML form.

Nina, however, is interested in creating a tool that enables her and her builders to create feedback forms with less custom programming. She is interested in creating a Web-based tool to create both the HTML feedback form and the CGI that handles it. Basically, she wants to build a feedback form "tool"-a tool that builds both the HTML and the CGI script. The first part of that, dynamic HTML is straight forward. She has experience generating HTML dynamically from the deli project previously. Her main task that involves new techniques is incorporating the methods of dynamic page creation to create CGI dynamically.

Analyzing the Expected Feedback

Nina does some research and finds that feedback forms she's seen have the following characteristics:

Nina realizes that she has to ignore some details from the dynamic CGI system. For example, when the CGI scripts are created by the dynamic CGI system, what programming language will the new CGI script be written in? She will stick with Perl. What is the default path of Perl on the system the new CGI script will be installed? Is it /usr/local/bin/perl, or /usr/bin/perl or something else unique? These are all configuration issues that can be handled by even more abstract installation methods than dynamic CGI systems. We'll cover those issues near the end of the chapter since they cover topics that dwell in the areas of shell script writing and software development in the UNIX environment.

Nina has opened up a can of worms really with this FeedbackTool. The problem she is faced with is providing an interface to allow the Web builder the capability to generate a custom feedback form with any number of questions. The configuration form for the FeedbackTool can't just ask to define the characteristics of say, five questions and be a tool that only builds five question feedback forms. What if the user only wants to ask one question, or a dozen? What Nina has introduced is a problem that can be solved with recursive objects. Chapter 22, "How to Build HTML On the Fly," revisits this situation. For now though, she has to resign herself to create an interface for the FeedbackTool that only creates feedback applications with a set number of questions. All the feedback forms created with the FeedbackTool will have five questions because she cannot use recursive CGI scripting to allow the developer to specify yet "another" question is needed. Figure 23.4 shows the flowchart for the FeedbackTool.

Figure 23.4: The main menu of the FeedbackTool lets you add a new "question," review the current feedback from you're building, create the CGI script and HTML page that implements your custom feedback form, or just stop the process.

The types of questions asked on the feedback forms can be one of the following types:

The list box, text box, and text area types can be preset with data like:

<input name="userEmail" value="Example: joe@foo.com">

So, the issue really is, should the configuration page for the FeedbackTool be a multi-part form that has a space for each type of question, all but one being ignored. Or, should the configuration form allow the user to first pick the type of question to appear next in the feedback application, and then offer the options on how it should be created. The latter option again involves recursive CGI applications that have not yet been covered. In order to show the salient features dynamic CGI systems, multipart forms are used in the configuration form of the FeedbackTool until the background with recursive CGI scripting techniques to make the whole FeedbackTool process interactive.

The FeedbackTool Application

What Nina is creating is called FeedbackTool. FeedbackTool is a CGI script that creates
a new CGI script for the feedback environment designed by the developer using the FeedbackTool.

FeedbackTool is a CGI script generating system. FeedbackTool itself is not the environment for making "feedback comments." That environment is being built by the FeedbackTool.

Nina will write the following requirements of the feedback tool:

  1. Allow the developer the opportunity to add any number of questions to the resulting feedback environment. We will allow the end result feedback tool to have as many questions on it as the developer wants.
  2. The types of each question can be
  3. The background color of the pages generated by the resulting feedback environment can be setup by FeedbackTool.
  4. Allow the developer to specify the e-mail address that feedback information is e-mailed to. Or specify a file name for feedback information to be appended to.
  5. Allow the developer to insert text messages (not questions) in between questions.

The flow of pages generated by the FeedbackTool are a cycle where the developer picks the option of adding a new question to the resulting feedback environment, or taking the questions built so far and use them to create the CGI script that will implement the resulting feedback environment.

Nina is going to build the FeedbackTool in stages. The first stage is a CGI script that will recursively ask for new questions and preserve that data as necessary until the second stage.

The second stage of writing the FeedbackTool is adding code that will take the preserved data about the questions specified by the developer and create the resulting CGI script. The second stage of development for the FeedbackTool will incorporate code for handling graphics, and the generic text messages between questions, and specifying the e-mail address of the person (or file name) for where the feedback will ultimately go.

Stage One of the FeedbackTool

Here is Nina's Perl code for the FeedbackTool (see Listing 23.5).


Listing 23.5  FeedbackTool.cgi-The FeedbackTool Application

#!/usr/local/bin/perl
require './web.pl';
%Form = &getStdin;

&beginHTML;

$nQ = $Form{'nQ'};

%QName = ('multi', "Multiple Choice",
     'text', "Simple Text Box",
     'yesno', "Yes or No",
     'area', "Text Area");

do {

 undef @preserve if defined(@preserve);

 print "<html>\n",
    "<title>Making Question</title>\n",
    "<body bgcolor=ffffff>\n";

 print "Your question type is: ",
     $QName{$Form{'makeQuestion'}},
    ".<br>\n",
    "The question text is: ",
    $Form{'questionText'},
    "<br>\n";
 
 push(@preserve, "newText=$Form{'questionText'}");
 push(@preserve, "newType=$Form{'makeQuestion'}");

 if ($Form{'makeQuestion'} eq 'multi') {

  for($i=1;$i<=5;$i++) {
   print "Option $i text: ", $Form{"multi$i"}, "<br>\n",
      " Value for option $i:", $Form{"value$i"}, "<br>\n";

   $p = "newMulti${i}=";
   $p .= $Form{"multi$i"};
   push(@preserve, $p);

   $p = "newValue${i}=";
   $p .= $Form{"value$i"};
   push(@preserve, $p);
  }
 }


 print "<form method=post action=\"/cgi-bin/feedbackTool.cgi\">\n";
 &preserveData(@preserve);
 print "<input type=\"hidden\" name=\"loop\" value=1>\n";
 print "<input type=\"submit\" value=\"Continue\">\n";       
 print "</form>\n";

} if ($Form{'makeQuestion'});
 
do {

print "<html>\n",
   "<title>Adding new Question (2)</title>\n",
   "<body bgcolor=ffffff>\n",
   "<form method=post action=\"/cgi-bin/feedbackTool.cgi\">\n",
   "What text do you want to appear before the \n",
   "input tag? ",
   "<input name=\"questionText\"><br>\n";

if ($Form{'questionType'} eq 'multi') {
 print "<p>\n",
    "For each option of the multiple choice, what\n",
    "text do you want before each option, and what\n",
    "\"value\" do you want to associate with each\n",
    "option?<p>\n";
 for($i=1;$i<=5;$i++) {
   print "Option $i:\n",
      "Text: <input name=\"multi$i\">\n",
      "Value if picked: <input name=\"value$i\">\n",
      "<br>\n";
 }
}

print "<input type=\"hidden\" name=\"makeQuestion\" ",
   " value=\"$Form{'questionType'}\">\n";
print "<input type=\"submit\" value=\"Continue\">\n";

&preserveData;

print "</form>\n";

} if $Form{'questionType'};

do {
 print <<"end of addnew";

<html>
<title>Adding new Question</title>
<body bgcolor=ffffff>

<form method="post" action="/cgi-bin/feedbackTool.cgi">

Type of Question:<br>

<ol>
 <li> <input type="radio" name="questionType" value="text"> Text Box<br>
   Example:<br>
   <input type="text"><br>
 </li>

 <li> <input type="radio" name="questionType" value="yesno"> Yes No buttons<br>
   Example:<br>
   <input type="radio" name="yesno" checked> Yes
   <input type="radio" name="yesno">No<br>
 </li>

 <li> <input type="radio" name="questionType" value="multi">Multiple Choice<br>
   Example:<br>
   <input type="radio" name="A" checked>A
   <input type="radio" name="A">B
   <input type="radio" name="A">C
   <input type="radio" name="A">D
   <input type="radio" name="A">E<br>
 </li> 

 <li> <input type="radio" name="questionType" value="area">Text Area</br>
   Example:<br>
   <textarea rows=3 cols=30>
   </textarea><br>
 </li>  
  
</ol>

<input type="submit" value="Continue">
<p>
end of addnew

&preserveData;

print "</form>\n";

} if $Form{'addNew'} == 1;
  
do {

  print <<"end of end";

<html>
<title>Thanks for Playing</title>

Thanks for playing.


end of end


} if $Form{'addNew'} < 0;

do {

 print <<"end of build message";


# to be continued


end of build message

} if !$Form{'addNew'};


do {
  print <<"End of wantnew";
<html>
<title>Do you want a new question?</title>
<body bgcolor=ffffff>

<form method="post" action="/cgi-bin/feedbackTool.cgi">

You have created $numQuestions so far.

Do you want to:
<input name="addNew" type="radio" value=1 checked>Add new Question<br>
<input name="addNew" type="radio" value=2>Review Current Feedback Form<br>
<input name="addNew" type="radio" value=0>Build CGI<br>
<input name="addNew" type="radio" value= -1>Terminate process<br>
<p>

<input type="submit" value="Continue">
<p>
End of wantnew

&preserveData;

print "</form>\n";

} if ($Form{'loop'});


exit;

sub preserveData {

 local(@list) = @_;


 if ($#preserve>0) {
  $nQ++;
  foreach $toSave (@list) {
   ($k, $v) = split(/=/, $toSave);
   print "<input type=\"hidden\" ",
      "name=\"preserveQ${nQ}_$k\" ",
      "value=\"$v\">\n";
   }
 }

 print "<input type=\"hidden\" name=\"nQ\" value=$nQ>\n";

 foreach $key (sort keys %Form) {
  if ($key =~ /^preserve/) {
    print "<input type=\"hidden\" ",
       "name=\"$key\" ",
       "value=\"$Form{$key}\">\n";
  }
 }
}

The FeedbackTool so far employs several techniques for data preservation and "recursive object" implementations.

Let's look at the script so far in greater detail and explain what is going on at each section before we ask Nina to go on and add more code to take care of the other features we want to incorporate into FeedBack tool.

We'll cut out the standard overhead code that is usually part of all CGI scripts. Each code snippet is in the same order sequentially from the original listing above.

We want to know right away how many "things" we've added to the user interface of the feedback environment. That data is kept in a hidden input type named nQ for "number of Questions".

$nQ = $Form{'nQ'};

We want to be able to assign descriptive names for each of the kinds of inputs that a developer can add to the feedback interface pages.

%QName = ('multi', "Multiple Choice",
     'text', "Simple Text Box",
     'yesno', "Yes or No",
     'area', "Text Area");

Okay, here's where we start to decide what we are going to do. The recursive nature of the script begins here since this script generates several kinds of pages and leaves behind "markers" on where to go next.

Once the user has picked the kind of question to add to the interface of the feedback form, we need to ask how the question should be labeled. Indeed, there are also some questions about the "question" itself. If the question added by the developer is a multiple choice question, we need to know the label for each of the options in the multiple choice, plus the values the developer wants to associate with the options in a multiple choice situation.

do {
 undef @preserve if defined(@preserve);

Just to be sure that we are not working with data that is old. This line should probably be taken out, but for the development process Nina will leave it in.

This snippet below shows that the developer using the FeedbackTool is informed about the kind of question they just added. Note the use of the %QName array to use the descriptive version of the name of the question type added.

 print "Your question type is: ",
     $QName{$Form{'makeQuestion'}},
    ".<br>\n",
    "The question text is: ",
    $Form{'questionText'},
    "<br>\n";

We need to preserve some information already, so lets add to our "preservation shopping list" some items that we want to preserve. We want to know later the type of question and the text of the question:

 push(@preserve, "newText=$Form{'questionText'}");
 push(@preserve, "newType=$Form{'makeQuestion'}");

Something special will happen if the question is a multiple choice question. We want to find out the labels to give each option of the multiple choice, plus the values to associate with each of those labels. We've already asked for that data in the last page in the chain of pages. Again, we are working in reverse from act of "adding a question." This section of the CGI script is finalizing up the data and preserving it for later processing.

 if ($Form{'makeQuestion'} eq 'multi') {
  for($i=1;$i<=5;$i++) {
   print "Option $i text: ", $Form{"multi$i"}, "<br>\n",
      " Value for option $i:", $Form{"value$i"}, "<br>\n";

And, we preserve all that information.

   $p = "newMulti${i}=";
   $p .= $Form{"multi$i"};
   push(@preserve, $p);

   $p = "newValue${i}=";
   $p .= $Form{"value$i"};
   push(@preserve, $p);
  }
 }

The action of this form goes back to itself. All the actions of all the HTML forms created with the FeedbackTool aim back to the same CGI, this one.

 print "<form method=post action=\"/cgi-bin/feedbackTool.cgi\">\n";

We're doing so much data preservation, that Nina has written a routine that will in bulk preserve data that is already hidden and new data.

 &preserveData(@preserve);

For testing, the hidden data named "loop" is set to 1. We'll see in the first iteration of the chain of pages generated by this CGI that to start the ball rolling we need a way to flag "start." Nina is using a temporary flag "loop" for that purpose. It will be replaced with a more robust method in the final version. To start the process of adding a question, the URL would be:

http://some.host.com/cgi-bin/feedbackTool.cgi?loop=1
print "<input type=\"hidden\" name=\"loop\" value=1>\n";

That's all there is for the "last stage" in the chain of pages for adding a question to the feedback environment.

We ask here what text should appear just before the question:

print "<form method=post action=\"/cgi-bin/feedbackTool.cgi\">\n",
   "What text do you want to appear before the \n",
   "input tag? ",
   "<input name=\"questionText\"><br>\n";

If the question we are decorating is a multiple choice question, we want to know what labels to put for each option of the multiple choice question. This bit of code will step through the five options we are allowing for a multiple choice question and ask the developer for the label for each option and the value to associate with that option. It is up to the person who reviews the actual feedback generated to interpret those values. For example, the multiple choice question setup by the developer might logically be:

How many hours a week do you read e-mail?

A.
Less than 1
B.
More than 1
C.
More than 5
D.
More than 10
E.
More than 20

The data associated with each option of this multiple choice question: less than 1, more than 1, more than 5, and so on is something the person actually reading the feedback will have to interpret. So, just as a caution-the FeedbackTool does not interpret the values specified by the developer for each option of a multiple choice question.

if ($Form{'questionType'} eq 'multi') {
 print "<p>\n",

    "For each option of the multiple choice, what\n",
    "text do you want before each option, and what\n",
    "\"value\" do you want to associate with each\n",
    "option?<p>\n";

 for($i=1;$i<=5;$i++) {
   print "Option $i:\n",
      "Text: <input name=\"multi$i\">\n",
      "Value if picked: <input name=\"value$i\">\n",
      "<br>\n";
 }
}

This little piece of code is embedding a hidden data input type storing the type of question that should be created. The major section of code above uses it as the "marker" to do its work.

print "<input type=\"hidden\" name=\"makeQuestion\" ",
   " value=\"$Form{'questionType'}\">\n";

And, again lets preserve any data if we have already added a question or text block to the interface-being-built.

&preserveData; 

The marker to get to this major section of code was setting the form variable "questionType." The major section of code below generated that marker. This current major section of code is the step after the major section of code below

} if $Form{'questionType'};

This is the step of the chain of pages that asks what kind of question to add. It doesn't concern the developer with the details about the question. They will have the opportunity later to specify the labels and values if necessary to associate with the question. In fact, you just saw in the major section of code above how that took place.

In this section of code, you are simply asking what kind of question to add to the feedback interface.

<form method="post" action="/cgi-bin/feedbackTool.cgi">
Type of Question:<br>
 <input type="radio" name="questionType" value="text"> Text Box<br>
 <input type="radio" name="questionType" value="yesno"> Yes No buttons<br>
 <input type="radio" name="questionType" value="multi"> Multiple Choice<br>
 <input type="radio" name="questionType" value="area"> Text Area</br>

And, again, preserve any data if you have already added a question or text block to the interface being built. At each page in the sequence generated by the FeedbackTool you must preserve data since each page stands alone as far as the Web browser and Web server are concerned. The only link, as discussed in the data preservation chapter (Chapter 20, "Preserving Data") is the data passed from page to page. You are doing that with hidden data types.

&preserveData; 
} if $Form{'addNew'} == 1;

The marker to get to this major section of code was to set the form variable "addNew" to 1. From the very first page in the sequence of pages, the developer specified which action to take. The possible actions are to

The main menu of the FeedbackTool lets you add a new question for the feedback form, review what feedback form you've built so far, or create the CGI script to implement the feedback form you built.

The marker to get to this section is to set the form variable "addNew" less than 0.

} if $Form{'addNew'} < 0; 

The next major section of code handles the event that the developer wants to actually use the information collected so far and build the CGI script implementing the feedback environment.

do {
 print <<"end of build message";
# to be continued
end of build message
} if !$Form{'addNew'};

The next major section of code generates the first page the developer will see when they start the process of creating a feedback environment. The page generated by the code below is also the page the user sees after going through a complete cycle of adding a question. It's like the "collect $200 as you pass go" square in Monopoly. You start there, and you also pass there when you make another cycle through the game.

<html>
<title>Do you want a new question?</title>

<form method="post" action="/cgi-bin/feedbackTool.cgi">

Do you want to:
<input name="addNew" type="radio" value=1 checked> Add new Question<br>

<input name="addNew" type="radio" value=2>  Review Current Feedback Form<br>
<input name="addNew" type="radio" value=0>  Build CGI<br>
<input name="addNew" type="radio" value= -1> Terminate process<br>
<p>

<input type="submit" value="Continue">
<p>

Even as you go through a new cycle, you need to preserve data.

&preserveData; 

The marker to get to this section of code, as explained above, was to set a form variable "loop" to 1. This method will change when Nina redoes some parts of the CGI script in the next stage of development.

} if ($Form{'loop'});
exit;

The rest of the script contains the function that helps do all the real work of generating HTML and preserving data.

sub preserveData {
 local(@list) = @_;

If you pass any data to this function to preserve, then you iterate through each item in the list and write out the HTML code to embed that data as a hidden type:

 if ($#list>0) {
  $nQ++;
  foreach $toSave (@list) {
   ($k, $v) = split(/=/, $toSave);
   print "<input type=\"hidden\" ",
      "name=\"preserveQ${nQ}_$k\" ",
      "value=\"$v\">\n";
   }
 }

A special naming convention is used for any data that you want to preserve. In the HTML code for preserving data with the hidden data type, you need to "name" the variable with a leading "preserveQ" and follow that with an underscore and the original name of the data variable. This is so we can look at all the form data at once from %Form and pick out the data that is supposed to be preserved. When the routines for actually generating the CGI script for the feedback environment is written, we will need to very easily pick out the hidden data from the form data. That is where the information is that tells the CGI generating routines what to do.

A special case for preserving the data is with the number of questions variable. We want to be very careful about how that is preserved since that value controls the count and labeling of hidden data.

 print "<input type=\"hidden\" name=\"nQ\" value=$nQ>\n";

This loop below looks through all the form data stored in %Form, and just as we started to mention above, that data is "re-hidden" if the name of each of the variables matches our pre-chosen pattern of a leading "preserve" in the name of the hidden variable.

 foreach $key (sort keys %Form) {
  if ($key =~ /^preserve/) {
    print "<input type=\"hidden\" ",
       "name=\"$key\" ",
       "value=\"$Form{$key}\">\n";
  }
 }
}

Site Specific Considerations

This chapter has started pretty high up, technically speaking, with ideas about states and objects and passing actions to objects. When it comes down to actually implementing a dynamic CGI system, the issues get a little more complicated than the abstractions about objects and states.

The need to implement a dynamic CGI system can come from different sources. Sometimes you end up developing a CGI script for a common situation, like a feedback form. Eventually, that feedback form is burdened with so many customizations that changing it for use in another environment is not so trivial.

The philosophy of dynamic CGI systems is to create a model for what the CGI script, a generic template of the CGI script.

The model is usually is a piece of code with labels positioned where substitutions are made for the actual, more flavorful, application.

To get a basic idea of a model for generating CGI scripts dynamically, consider any Perl CGI script. It has a common usage:

#!/usr/local/bin/perl

This is the absolute path to Perl on the server. But, this path isn't always where Perl
is located. Your server might not have Perl installed or it's installed someplace "non-standard." If you are trying to generate CGI dynamically, you need to be very complete about doing so, right down to the location of Perl on the server. Can you use dynamic CGI system techniques to solve this problem? Sure.

Consider a CGI script:

#!/usr/local/bin/perl
require './web.pl';
%Form = &getStdin;
foreach $variable (sort keys %Form) {
  print "Key: $variable Value: $Form{$variable} <br>\n";
}
exit(0);

This CGI script is specific to the server it was written for. The path to Perl for instance is set to /usr/local/bin/perl. The library included was web.pl. The function to grab the HTML form data is getStdin. Could all these specifics be set dynamically? Yes.

Let's build a CGI script to automate the creation of the CGI script above for any Web server environment.

The system will consist of a tool and a result. The tool is the CGI script which generates the result. The result is the customized CGI script that acts like the model above. The script above really isn't a model exactly since it doesn't have any generic labels to be replaced. The script above is an example of what the tool will generate.

The tool is a CGI script itself, and so it has the same properties of all CGI scripts: generate documents and perform system tasks. The documents it generates are split up into three pages.

The system tasks done at the same time as generating the third page built by the tool are to create the result CGI.

The third type of page built by the tool is a thank you page for acknowledging the successful completion of the tasks done while generating the third page.

The tool generates a CGI script based on just a few questions from the user for the cosmetic look of the result CGI. But the main configuration decisions are done by poking around the system.

For example, the path for Perl is determined by looking in a long list of directories that it might be located. Even if it can't find the path, the confirmation page will ask for help.

The confirmation page is like the receipt for the dynamic CGI process. The user visits the setup HTML form page, makes selections. Then the user is presented with a confirmation page with the same form, the fields all pre-filled in with the data from the previous form. The confirmation page also asks the big question: "Is this OK?"

If so, then the next and final page generated by the tool, the thank-you page, is to plug up the "HTML void." Before the thank-you page is generated, it's possible that the CGI script has done all the system tasks necessary to create the result CGI. After the result CGI is created, the objective is accomplished. Why do we need to generate the thank-you page?

The acknowledgment (thank you) page is a filler page because it offers nothing new for the user to do or configure. It's there to fill the space. What value it does however is be a neutral page to launch into other pages.