|
The text and programs here are from the first edition of CGI Programming 101. This has been replaced by the 2nd edition; please click here to view the updated material from the 2nd edition. |
In the last chapter you learned how to decode form data, and mail it to yourself. However, one problem with the previous script is that it doesn't have any error-checking or specialized processing. You might not want to get blank forms, or you may want to require certain fields to be filled out. You might also want to write a quiz or questionnaire, and have your script take different actions depending on the answers. All of these things require some more advanced processing of the form data.
All that's required here is to know how to test conditions in Perl. Probably the main one you'll use in a form-handling script is the if-elsif condition:
if ($varname eq "somestring") { ...do stuff here if the condition is true } elsif ($varname eq "someotherstring") { ...do other stuff } else { ...do this if none of the other conditions are met }Theelsifandelseblocks are optional; if you are only testing whether a particular variable is true or not, you can just use a single if block:
if ($varname > 23) { ...do stuff here if the condition is true }In Perl there are different conditional test operators, depending on whether the variable you want to test is a string or a number:
Test Numbers Strings $x is equal to $y $x == $y $x eq $y $x is not equal to $y $x != $y $x ne $y $x is greater than $y $x > $y $x gt $y $x is greater than or equal to $y $x >= $y $x ge $y $x is less than $y $x < $y $x lt $y $x is less than or equal to $y $x <= $y $x le $y Basically, if it's a string test, you use the letter operators (eq, ne, lt, etc.), and if it's a numeric test, you use the symbols (==, !=, etc.). Also, if you are doing numeric tests, keep in mind that $x >= $y is not the same as $x => $y. Be sure to use the correct operator!
Let's try it. Copy your mail.cgi to a new script called mail2.cgi, and insert this conditional test before the open(MAIL) statement:
if ($FORM{'name'} eq "") { dienice("Please fill out the field for your name."); }This condition takes advantage of the dienice subroutine we wrote before. Now, if you submit a blank form, you'll get the error message.You can extend this to test for multiple fields at the same time:
if ($FORM{'name'} eq "" or $FORM{'email'} eq "" or $FORM{'age'} eq "") { dienice("Please fill out the fields for your name, age, and email."); }The above code will return an error if any of the name, email, or age fields are left blank. The conditions are separated by the or operator (which may also be written as ||) - if any of (test1 or test2 or test3) is true, then the condition is met.
Handling Checkboxes
You may want to include checkboxes in your form, to allow the viewer to select one or more options. But how do you decode these inside your CGI?If you just want to display them in your email message, you can just print them like you would any text field; each checkbox has a different name. Open a new HTML file, and call it colors.html. Enter the following form:
<html><head><title>colors</title></head> <body> <form action="colors.cgi" method="POST"> <h3>What are your favorite colors?</h3> <input type="checkbox" name="red" value=1> Red<br> <input type="checkbox" name="green" value=1> Green<br> <input type="checkbox" name="blue" value=1> Blue<br> <input type="checkbox" name="gold" value=1> Gold<br> <input type="submit"> </form> </body></html>Source code: http://www.cgi101.com/class/ch5/colors.html
Here's how it looks on the screen:
This example lets the visitor pick as many options as they want - or none, if they prefer. While you can set the value="whatever" part of the checkbox field to any value you want, if you use integers, it will mean less code inside the CGI.
Now let's write the CGI to process the above form. Call it colors.cgi:
#!/usr/bin/perl print "Content-type:text/html\n\n"; read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $FORM{$name} = $value; } print "<html><head><title>Results</title></head>\n"; print "<body>\n"; print "<h2>Results</h2>\n"; @colors = ("red","green","blue","gold"); foreach $x (@colors) { if ($FORM{$x} == 1) { print "You picked $x.\n"; } } print "</body></html>\n";Source code: http://www.cgi101.com/class/ch5/colors.txt
NOTE: if you used value=1 in your form, then your CGI can test it with "if ($FORM{$x} == 1)". But if you put quotes around the 1, such as value="1", then your CGI will not work unless you change the == operator to an eq:
if ($FORM{$x} eq "1") { ...do whatever }Handling Radio Buttons
Radio buttons differ from checkboxes in that you can have several buttons that share the same field name in the form itself - thus allowing the viewer to only select one of a series of options. To distinguish each option, the buttons themselves must have different values.Let's try it. Take your colors.html file, and copy it to colors2.html. Then edit it and replace the checkbox fields with the following:
<h3>What is your favorite color?</h3> <input type="radio" name="color" value="red"> Red<br> <input type="radio" name="color" value="green"> Green<br> <input type="radio" name="color" value="blue"> Blue<br> <input type="radio" name="color" value="gold"> Gold<br>Source code: http://www.cgi101.com/class/ch5/colors2.html
Here's how it looks on the screen:
This is similar to the checkboxes form. However, in this case, each radio button has the same field name, but a different value. It's easiest to set the value to be relevant to the name of the thing being picked - in this case the values are set to the name of the colors themselves. Radio buttons can be handled in Perl fairly simply. Copy your colors.cgi to colors2.cgi, and replace the foreach loop with the following line:
print "Your favorite color is: $FORM{'color'}<br>\n";Source code: http://www.cgi101.com/class/ch5/colors2.txt
You see here why it is best to set the value to something meaningful - this lets you just print out the radio button and its value, without having to also store another list inside your CGI to show what each button means.
Let's take this one step further. Say you not only want to tell the viewer what color they picked, but you also want to show it to them. Edit your colors2.cgi and replace the existing print statements with the following code:
%colors = ( "red" => "#ff0000", "green" => "#00ff00", "blue" => "#0000ff", "gold" => "#ffcc00"); print "<html><head><title>Colors</title></head>\n"; print "<body bgcolor="$colors{$FORM{'color'}}">\n"; print "<h2>Your favorite color is: $FORM{'color'}</h2><br>\n"; print "</body></html>";Source code: http://www.cgi101.com/class/ch5/colors2a.txt
The above actually sets the background color to whatever color you picked. It's using a hash called
%colors, whose keys are the same as the data value of the radio buttons in the form itself. The values of the hash are hex codes for those colors.Handling SELECT Fields
Select fields may be handled almost exactly the same as radio buttons. A select field, in your HTML page, is a pull-down menu like this:
The HTML to generate the above is as follows:
<select name="color"> <option value="red"> Red <option value="green"> Green <option value="blue"> Blue <option value="gold"> Gold </select>As with radio buttons, you can print out the selection just as it was made in the form:
print "Your favorite color is: $FORM{'color'}<br>\n";This will only work if your select statement is a single-value select (that is, your visitor can't select more than one item from the selection list). For handling multi-value selects, see below.Multiple-choice Selects
The reason our above form-handling code doesn't handle multi-choice selects is that we're assigning a single $value to each field name from the form:
$FORM{$name} = $value;If you have a multiple select, like this:
This code allows the user to pick more than one city from the list:
<select name="cities" multiple size=3> <option>Dallas <option>Houston <option>Seattle <option>Portland <option>Denver </select>Then when your form data is passed on to the CGI, you have several instances of cities="cityname". So if $FORM{'cities'} already exists and has a value in it, and you send another value, the old value just gets overwritten by the new one.The solution to this is to handle the multi-selects differently, by storing them in their own array, rather than in the $FORM hash. Here's how you might do it:
@cities = (); read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; if ($name eq "cities") { push(@cities, $value); } else { $FORM{$name} = $value; } }Source code: http://www.cgi101.com/class/ch5/mform.txt
Working example: http://www.cgi101.com/class/ch5/mform.html
Now you can use the @cities array for any special processing of the multi-field city data. Everything else is stored in
%FORM.Survey Form and CGI
Let's take what you've learned so far and put it to practical use: a survey form and its corresponding CGI. This type of form adds interactivity to your site, whether you're doing a one question poll-of-the-day, or a lengthy survey of your site's readers.Create a new HTML form, and name it survey.html. Enter the following form:
<html><head><title>Survey</title></head> <body> <h2>Survey</h2> <form action="survey.cgi" method="POST"> Enter your name: <input type="text" name="name" size=30><p> Your email address: <input type="text" name="email" size=30><p> How did you reach this site? <select name="howreach"> <option value=0 selected>Choose one... <option value=1>Typed the URL directly <option value=2>Site is bookmarked <option value=3>A search engine <option value=4>A link from another site <option value=5>From a book <option value=6>Other </select><p> How would you rate the content of this site?<br> Poor <input type="radio" name="rating" value=1> 1 <input type="radio" name="rating" value=2> 2 <input type="radio" name="rating" value=3> 3 <input type="radio" name="rating" value=4> 4 <input type="radio" name="rating" value=5> 5 Excellent<p> Are you involved in any of the following? (check all that apply):<br> <input type="checkbox" name="des" value=1> Website Design<br> <input type="checkbox" name="svr" value=1> Web Server Administration<br> <input type="checkbox" name="com" value=1> Electronic Commerce<br> <input type="checkbox" name="mkt" value=1> Web Marketing/Advertising<br> <input type="checkbox" name="edu" value=1> Web-related Education<br> <p> Any other comments?<br> <textarea name="comments" rows=5 cols=70 wrap="VIRTUAL"> </textarea> <p> <input type="submit"> </form> </body></html>Source code: http://www.cgi101.com/class/ch5/survey.html
Save it. Now create survey.cgi:
#!/usr/bin/perl print "Content-type:text/html\n\n"; read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg; $FORM{$name} = $value; } # Since the "how you reached this site" list was saved as # a number, we need a hash to translate it back to English: %howreach = ( 0 => "", 1 => "Typed the URL directly", 2 => "Site is bookmarked", 3 => "A search engine", 4 => "A link from another site", 5 => "From a book", 6 => "Other" ); print <<EndHTML; <html><head><title>Results</title></head> <body> <h2>Results</h2> Here's what you entered:<p> Your name: $FORM{'name'}<p> Email: $FORM{'email'}<p> How you reached this site: $howreach{$FORM{'howreach'}}<p> How you'd rate this site (1=poor,5=excellent): $FORM{'rating'}<p> EndHTML %boxes = ( "des" => "Website Design", "svr" => "Web Server Administration", "com" => "Electronic Commerce", "mkt" => "Web Marketing/Advertising", "edu" => "Web-Related Education" ); print "You're also involved in the following:<br>\n"; foreach $key (keys %boxes) { if ($FORM{$key} == 1) { print "$boxes{$key}<br>\n"; } } print <<EndFoot; <p> Your comments:<br> $FORM{'comments'}<p> </body></html> EndFootSource code: http://www.cgi101.com/class/ch5/survey.txt
Working example: http://www.cgi101.com/class/ch5/survey.html
Save and chmod it, and try filling out the survey in your browser. This example posts the results to the page, but you can just as easily email yourself a copy of the results, using the mail code in chapter 4. In this case all you need to change, after the MAIL handle is open, is the print <<EndHTML statements, to:
print MAIL <<EndHTML;This will redirect the subsequent text to the mail message, rather than standard output.It's more likely you'll want to write the data to a file, however, so you can analyze the results later. We'll cover reading and writing files next.
Resources
Visit http://www.cgi101.com/class/ch5/ for source code and links from this chapter.