Creating a Multiple Choice quiz System, Part 3

 in
We continue to improve our quiz file program, by adding an editor.
Displaying the Quiz

Before we get to work coding, let's figure out how all of this works. We invoke edit-quiz.pl in a number of different ways, using both GET (i.e., entering its URL in a browser window or clicking on a hyperlink within a page of HTML) and POST (i.e., when we click on a submit button at the bottom of an HTML form).

If edit-quiz.pl is invoked using GET without any arguments, we are asked which quiz we wish to create or edit. Entering the name of the quiz file and pressing Return submits it to the program, which then receives this argument—again using GET--in the query string.

The query string, for those of you relatively new to CGI programming, is the term for anything following the question mark in a URL. It allows the passing of simple arguments to a CGI program without having to use POST, a more complicated and sophisticated protocol for passing information. Thus, if someone invokes the program:

http://www.fictional.edu/cgi-bin/program.pl

the query string is null, because there is no argument. But if someone invokes the CGI program:

http://www.fictional.edu/cgi-bin/program.pl?foobar
the argument is foobar. If we are using CGI.pm, a Perl module for writing CGI programs (available from CPAN at http://www.perl.com/CPAN), we can theoretically retrieve the contents of the query string using the query_string method, as in:
my $query = new CGI;
my $query_string = $query->query_string;
For reasons that I don't completely understand, the query_string method returns the query string with a prepended keywords=, as if the query string had been submitted to our CGI program in an HTML form element named keywords. While this can sometimes come in handy, for the most part, I find it a surprising quirk in an otherwise excellent package.

If CGI.pm is going to treat the query string as a parameter named keywords, we have to retrieve its value in the same way as we would other parameters, namely:

my $query = new CGI;
my $query_string = $query->param("keywords");

which might seem a bit odd at first, but you get used to it.

We determine whether our program was invoked via GET or POST using the method request_method within CGI.pm. In other words, we can do the following:

my $query = new CGI;
my $request_method = $query-7gt;request_method;

At this point, the variable $request_method contains either the string GET or POST, depending on how the program was invoked. The main difference between these two invocation methods is how arguments are passed to the program: GET sends all of the variable values in the query string, while POST sends them via stdin, the file handle associated with standard input. Luckily, CGI.pm frees us from having to deal with these methods and invisibly hands us the parameters regardless of their source.

In any event, if our program is invoked with any value in the query string, we print out an HTML form containing the contents of the quiz file with that name or a blank HTML form allowing the user to create a quiz of that name. You can see that program in Listing 1.

There are several things to note in this program. First of all, we need to tell the program the maximum number of questions that each quiz can contain. We do this by setting a global variable, $MAX_QUESTIONS, at the top of the program. Either very short or very long quizzes can be allowed for by changing this variable.

Also, notice how we manage to create a page of HTML that invokes our program with an argument in the query string. We use the <ISINDEX> tag, which has been all but forgotten on the Web, mostly because it creates an ugly text box whose instructions are difficult or impossible to change and rarely relevant to the subject at hand. Nevertheless, <ISINDEX> comes in handy if you want to provide a program with a mechanism to feed a user-defined argument to itself.

In addition, we create a new instance of QuizQuestions based on the name of the quiz that we received from the user in the query string. Once the instance of QuizQuestions is created, we instruct it to load its contents from disk. Of course, if this is a new quiz, then there is nothing to load, and this is noted in the error message returned by the loadFile method. We don't care if there was an error opening the file—if the quiz file exists, the contents are displayed in the HTML form, but if it does not exist, it is treated as a new quiz.

Of course, it is not a good idea to ignore error messages altogether. But the error messages returned by the loadFile method are fairly primitive, indicating whether the file was successfully loaded. Better error messages might distinguish between an inability to find the file in question, a quiz file that exists but cannot be read and a quiz file containing errors. But for now, this is all we've got, so we will have to live with it.

We insert the current value of each HTML form element by placing the value inside a variable. One of the nice things about Perl is that uninitialized variables default to the empty string (""). This means that if we have not set a particular question or answer, things don't crash. Rather, since we get the empty string back from the variable, we can stick the variable into the form element's value attribute, thus resetting the form element's value.

We use a bit of cleverness to indicate which element of the selection list (which is used to indicate the correct answer) should be selected by default. Here is the code:

my $letter = "";
foreach $letter
("a","b","c","d")
{
   print "<option ";
   print "selected " if ($letter eq $correct);
   print "$letter>$letter\n";
}
print "</select>\n";

In this code, we simply iterate through all four possible correct answers, inserting the word selected inside of the <option> tag where it is appropriate.

As you can see, it is not difficult to create a program that displays the contents of a quiz file. If we want to create an editor, we need to write the second half of the program, namely the part that takes the submitted form contents and saves them to disk. Luckily, the way we have organized our HTML form makes this fairly trouble-free.

______________________

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix