This lecture covers two topics: Advanced Form Techniques, and Dynamic Graphics. Dynamic Graphics concerns software which draws pictures as you need them. We normally think of this as a Java-ish kind of thing to do, but CGI can do it too. You'll learn how, later in the lesson. First, however, the forms!
We use Castro's book, Chapter 12, in this lecture.
Advanced Forms: The Master Form Technique
The advanced form technique in which I'm most interested, is the one which I described earlier in class: how do you build a server which can read files containing special HTML (e. g. to provide a "catalog" of products), mix this information with a current price table, and emit a working form? I call this the Master Form technique.
Commercial products such as Cold Fusion can be used to realize this result. Cold Fusion is "middleware", which is to say, software that does more than canned programs can do, but supposedly requires less effort than programming would require. Since you're mostly computer science majors rather than Management Information Science canned- program- majors, I'm going to teach you the Macho Way to Program It Yourself.
Unfortunately, I have found no way to use CGI.pm to support the Master Form technique. CGI.pm presumes that you are going to represent your form directly in Perl, with CGI.pm function calls being used to construct the strings that you print out to make the HTML. CGI.pm is best used for those portions of one's on-line commerce system that concern getting the user's name, address and payment information - the parts that don't change much from one month to another. You can use Master Forms alongside CGI.pm, as we will see.
If we want to be able to read a master form, insert prices into it and then emit it, there are great advantages to being able to have the master form itself be in HTML - or close to it. You can use tools like Netscape Composer or even Microsoft Word (shudder) to lay out nice-looking forms - or at least the tables, captions and graphics which make the forms look nice. You will then have to hand-edit the HTML to insert the input fields, pulldown menus, etc. that you need. Finally you will insert the control markers that show your script where to stick in pricing and inventory information.
Master Forms
This section involves a script which dynamically modifies a master document,
converting non-HTML into legitimate HTML. The two choices are
A warmup exercise. To begin with, we need some practice with hash tables and search patterns. Let's assume that you have code (I gave it to you last week) which can read key=value pairs from a text file and put them into a hash named %salary. So, for instance, if you executed
print $salary{"Smith,John"};
you should see printed
24000
In another array named @employees, you have a list of the employees' names, in the form shown above, without spaces, like this:
Lastname,Firstname
Write a simple code loop which would produce a HTML table displaying all your employees and their salaries.
A second exercise. Write code which goes through an array @text, each element of which is a string. Your routine prints out these lines, after modifying them as follows. Any time a phrase of the following form is encountered, the stuff between the double tildes is replaced by HONK HONK.
This is a ~~sample phrase~~ embedded in a line of text.
After your routine runs, this line is printed out as
This is a HONK HONK embedded in a line of text.
If you get stuck here, there is a hint below, titled HINT FOR SECOND EXERCISE.
Main Design Exercise.
If you understood what you did in the above two exercises, you're now ready for the design problem of the day.
The Widget Company wants to develop a server which can put up any catalog page, and insert into the page the current price and inventory information. It is also desired to be able to put up "special notices", such as flagging an item as being on closeout.
Therefore, we will have three associative arrays pre-loaded with data:
$PRICE{$item}, $INVENTORY{$item} and $SPECIAL{$item}.
The $item selector will consist of a product code, which is of form ‘C4324-5’, so we have to treat it as a string.
We want three kinds of tags that we can embed in our master form. Since the 'tilde' character ~ has no business occurring twice in normal HTML, we use it to define some special flags, as follows:
~~pC4324-5~~p will cause the price of that product to be displayed.
~~iC4324-5~~i will cause the inventory of that product to be displayed, and
~~sC4324-5~~s will cause that product’s special information – including any imbedded HTML-to be displayed.
Your code simply reads through the master form (using a while loop or a foreach loop), performs the necessary substitutions, and emits the resulting code. It's up to you to decide how you want to read the master form file into the program- one line at a time, or the whole thing into an array.
Here's an example of a very small Master Form for use with the above
technique. It uses only the price insertion system.
You should probably develop the code for one tag first (like price),
and then after your group agrees that this is the way to go, modify it
to handle three tags. The easiest way to do three tags is just to take
three passes through each line of the data.
***********************************************
<html>
<head><title>The Widget Company Online Catalog</title>
</head>
<body>
<form METHOD=POST ACTION="http://www.creat.cas.ucf.edu/~schmoe/cgi-bin/cgiwrap/exercise1.pl">
<h1>Please Tell Us About Yourself</h1>
<p>
Your last name? <input size=25 maxlength=25 name="lastname">
<! put all kinds of address fields here.>
<p>
<h1>Today We Sell Two Things Proudly</h1>
Please type the desired quantity of each item in the space next to
its description.
<p>
<table>
<tr>
<td>Acme Widgets, One Size Fits All: ~~pwidget~~p </td><td>
<input size=5 maxlength=5 name="widgets" > </td></tr>
<tr>
<td> Widget Cleaner: ~~pcleaner~~p</td><td>
<input size=5 maxlength=5 name="cleaners" > </td></tr>
</table>
<input TYPE="submit" NAME=SubmitAction VALUE="Submit Your Order">
</body>
</html>
**************************************************
***********************
***********************
HINT FOR SECOND EXERCISE: Use the split operator on your working variable
($textline, perhaps) to split it into three pieces: head, middle and tail.
Then print out head, HONK HONK and tail.
***********************
***********************
The Problem of Persistence. (Chapter 12 of Castro's book.)
You will (if thoroughly sober and awake) perhaps remember that one of the main reasons for introducing the CGI.pm library was to make it easy to have forms which 'return from the dead' - that is, when you run your server to error check the forms, and re-emit the form, its previous values are automatically in place. This technique only works if you are always re-displaying every field in your form, however. Let's review WHERE the data is, at any given time.
CLIENT: REQUEST THE FORM. Your browser accesses a URL which might contain the source document itself, or might access a server that makes up the form. The form may come to you with default values in some fields.
CLIENT: INITIAL FORM-FILLING. You type in values, or click buttons or pull down menus, to specify values for each variable. These are transmitted in the output url-encoded stream as key-value pairs, like
lastname=Moshell&firstname=J.+Michael
with + replacing spaces, as we mentioned before.
SERVER:INITIAL CHECKING. The server first reads the form into a key-value hash. If you're using CGI.pm in functional form, it's a hidden hash which we refer to via its accessor function param(). Or maybe you wrote your own code to read it into %FORM. This is a good idea, if you're going to use the Master Form technique.
Now the server checks what your user did. If they did everything right, you can proceed to the next step, using the information from %FORM. But let's assume that you find a problem with one of the forms, and want to send the user an error screen. You need to stash everything in %FORM somewhere. You decide to send it back to the client, in hidden variables, so it'll come back in again when they acknowledge their error.
Here's a code loop stolen and modified from Castro's page 164:
foreach $key (keys %FORM)
{
print "<INPUT TYPE=hidden NAME=$key VALUE=$FORM{$key}>\n";
}
You include this along with the various Print commands which tell the user what is wrong. Perhaps that screen says
*******************
SubmitAction=Partial+order
or
SubmitAction=Delay+order
or
SubmitAction=Cancel+order
depending on which Cancel button is pushed.
Now, hidden inside the above form, we have stashed all the information about lastname, firstname, address, etc. because of the foreach $key loop. So, when your user hits any one of the Submit keys, the whole form pours back into the server.
SERVER: RECEIVING RESPONSE FROM BOOBOO SCREEN. When the server reads the response, it streams the whole thing into $FORM again. But since you had hidden variables, it gets not only the SubmitAction=something pair, but also lastname=Moshell&firstname=Michael& whatever else was in the original form.
Now if you are smart, you will set up your form so that if and when it is re-presented to the client, the user's lastname reappears! By now, with your familiarity with our double tilde imbedding technique, you can probably figure out exactly how that's done.
Exercise 3: How do we emit a form so that any existing values in $FORM are inserted as default values, just as though we were playing the role of CGI.pm? Write down a line of HTML which (if properly treated by your script) would restore the user's last name.
Now describe, in English or Perl, what you should be putting into the
place where the tag is removed by your server. A hint is found below the
section on graphics, which is coming right up.
print "line 1\n";
print "line 2\n"; etc, etc.,
you can do the following:
print <<EndMarker
line 1
line 2
whatever you want to print
EndMarker
When the symbol that you declared right after << occurs, the input to the print statement terminates (without, obviously, printing EndMarker.)
Animation can be accomplished by Java or Javascript, with the advantage that you are not waiting on the Internet’s highly variable timing. However, if you are trying to animate something that depends on new data - such as a stock ticker tape - you have to ask the server for information anyhow. So Java has no special advantage in that case.
Animation can be controlled by the client or the server. If the client asks to run a script over and over, that would obviously produce animation - if the server sent up something different each time. Another way to (passively) produce animation is to use an animated GIF, which actually stores different information in each of several frames. But that would hardly work for a stock ticker tape, would it? So we have to establish a repetitive dialog of some kind, between the client and server.
Client Pull. To ask for a script-generated graphical image, one simply calls the script that's supposed to draw or supply the image, inside an IMG tag, like this:
print <<End_of_HTML;
... various HTML details
... down to:
<IMG SRC="/cgi-bin/digital.pl">
....some more HTML
End_of_HTML
This will, however, only get ONE copy of the image. (We'll discuss in a later lecture, how your script can actually create an image.) To get multiple copies, you have to tell the browser to repeatedly ask for the document, like this:
<META HTTP-EQUIV="Refresh" CONTENT=5>
This will generate a call every 5 seconds, by the brute force method of re-fetching and rendering the entire HTML document - including its dynamic call or calls. In fact, Refresh only works once. But in the new copy of the HTML that is fetched, there should be embedded another identical copy of the Refresh meta-command.
Server Push. If the browser is "warned" to stay open to continuous input, the server can be set up to send the animation information continuously. Here’s the setup command:
Content-type: multipart/x-mixed-replace; boundary=End
--End
Content-type: image/gif
Image #1
--End
Content-type: image/gif
Image #2
--End
...
--End--
The first line declares that each successive image will end with the characters "--End". This continues until the boundary symbol appears with two "-" characters on either side of it - like --End-- Everything else that came in, before --End, was interpreted as bytes of a GIF image.
The server push example code:
******************************************************************
#!usr/local/bin/perl
$|=1;
$webmaster="shishir/@bu\.edu";
$boundary_string="\n"."--End"."\n";
$end_of_data = "\n"."--End--"."\n";
$delay_time = 1;
@image_list = ("image_1.gif", image_2.gif","image_3.gif","image_4.gif","image_5.gif")
$browser = $ENV{‘HTTP_USER_AGENT’};
for ($loop=0; $loop<scalar(@image_list); $loop++)
{
&open_and_display_GIF($image_list[$loop]);
print $boundary_string;
sleep($delay_time);
}
print $end_of_data;
exit (0);
####################
sub open_and_display_GIF
{
local ($file) = $_;
local ($content_length);
if ((open (FILE, "<". $file))
{
$content_length = (stat (FILE) [7]); # The 7 denotes
file length. See comments below for other file stats.
print "Content-type: image/gif", "\n";
print "Content-length:",$content_length, "\n\n";
print <FILE>;
close (FILE);
}
else
{
print "File Access Error - Cannot open graphic
file $file!\n";
}
}# End open_and_display_GIF
# End of server-push example code
******************************************************************
In our next CGI lecture, we'll explore "GhostScript" which is a tool
whereby Perl can create GIF images from scratch. It's a kind of Postscript
interpreter.
***********************
***********************
HINT FOR THIRD EXERCISE: [Heck, it's almost the ANSWER to Exercise 3.] Your tag for this purpose, which might look like ~~vlastname~~v, will be replaced (by your script) by this:
value="Moshell"
assuming that $FORM{"lastname"} was Moshell at that point. The main thing that ~~v does differently from ~~p or ~~i or any other tag, is that it must stick in both the value from %FORM and the keyword "value=" to make it all work.
Now of course this little snippet is going into an <input ....> field, so you still have some work to do, to get it right.
***********************
***********************
Back to previous lecture
Forward to next lecture
Back to the Index
Back to the Syllabus