Using What We've Learned

This month Mr. Lerner shows us how to set up a web site using many of the techniques he's taught us over the past months.
Tracking Users

Now users can enter information about their birthdays into the system. But wait—at the beginning of the column, I said that we were going to make it possible to keep track of users' comings and goings. One easy way to do that is to use HTTP cookies, small pieces of data stored on the user's computer that are sent to the site that set them. CGI programs can set cookies whenever they return an HTTP response to a user's browser. Whatever cookies were set are then returned upon each subsequent visit to that web server. In this way, they can be used as variables, albeit variables that might be modified or removed by users worried about their privacy, or who might come to our site from a friend's computer.

Our cookie will have to be a unique identifier that can be used to bring up the user's entry from the “birthdays” table. We thus have two options—the user's e-mail address, which is guaranteed to be unique because it is a primary key, and person_id, which is automatically incremented each time a new entry is added to the table. We will use person_id, but there is no reason why you could not use the “email” column. Indeed, given that “email” is a primary key, you could even do away with the person_id column—except that if you were to create other tables that refer to individual users, keeping track of an integer (such as person_id) is much more efficient than using their full e-mail address.

How can we retrieve the ID that was added to a row that we might add? The most obvious way is to retrieve the row that we just entered, creating and sending an SQL query to the MySQL server. But MySQL has an easier way to do this—after sending our query, we can ask for $sth->insertid. $sth is the “statement handle,” an object that allows us to send and retrieve information about an individual SQL query and statement, and insertid is one of the methods that $sth provides.

Once we know the value of person_id, we can create a cookie with the following two lines of Perl:

my $cookie = $query->cookie(-name => "person_id",
        -value => $sth->insertid);

The cookie (now stored in $cookie) is sent as part of the HTTP header returned by the CGI program to the user's browser. Thus, the original version (see Listing 2) of our program sent a basic MIME header at the beginning of the program's execution with the command:

print $query->header("text/html");
But the new version of our program will have to move that to a later portion of the program, after we have already sent our query to the database. In addition, we will have to modify our statement such that it sends the cookie along with the MIME information in the header. This can be accomplished with the following code:
print $query->header(-type => "text/html",
                         -cookie => $cookie);
When the above code runs, it sends both the MIME header that describes the type of output that we are sending to the client, and the information describing the “person_id” cookie that we want to set. Every time this user visits our site in the future, we will be able to retrieve the value of “person_id”, and thus look the user up in a table in our database.

You can see the results of changing our program in Listing 3. The modifications are fairly small, but they ensure that a cookie is returned to the user's browser whenever we successfully add a row to the database. (When no row is added to the database, the header remains as before, sending the MIME header but nothing else.)

Retrieving the Data

The final part of our system will bring cookies and databases together with another technique we discussed several months ago, Perl/HTML templates. Templates are pages of HTML with small snippets of Perl interspersed between the HTML tags, accessed via a short program that uses the Text::Template module to turn the Perl into text. This allows HTML pages to perform database lookups, calculations and other elements that require computation too difficult to accomplish with server-side includes. Unlike straight CGI programs, templates can be edited by your site's designers and HTML coders, removing the programmer as a bottleneck to changing the HTML that a program produces.

In Listing 4, you can see wrapper.pl, a CGI program that turns templates into HTML. (A version of wrapper.pl published in a previous issue of LJ was not tested thoroughly, and contains several bugs that were fixed in this version.) Assuming that wrapper.pl is installed on your system and that the file /home/httpd/html/birthdayhp.tmpl is a valid template, you should be able to request

/cgi-bin/wrapper.pl?/home/httpd/html/birthdayhp.tmpl

In other words, wrapper.pl should be invoked with a single argument, the name of the template that should be evaluated and then returned.

This version of wrapper.pl allows us to pass variables to the template using the LJ package (as described in the manual pages for Text::Template). This allows us to pass variable values from wrapper.pl to templates. In general, we would not want to pass variable values from wrapper.pl to a template, since the template should be somewhat isolated from its surroundings and should be allowed to assign its own variables. But in this case, we do want to pass one value, that of $query (the CGI instance), which allows us to access information passed to wrapper.pl per the CGI specification. This includes the “cookie” method, which allows us to retrieve cookies that our server has set in the past.

I have included a simple version of birthdayhp.tmpl in Listing 5, so that you can see how easy it is to include Perl inside HTML. There is a performance penalty for serving documents in this way, since you are forcing an invocation of Perl each time a template is viewed on your system. But that drawback is often counterbalanced by a template's versatility and ease of inclusion in a site. A large Web site's editorial and production staffs can thus modify the site's content and design without disturbing programs necessary for the site to run. The programs are surrounded by curly braces, making them easy to spot in an HTML file.

One thing to remember when dealing with templates is that the output from each Perl fragment is inserted into the resulting HTML. If you wish to send the text “Hello, there” to the user's browser from within a Perl fragment, in order for things to work correctly you must use:

{
    "Hello, there"
  }

or a slightly more formal version:

{
    my $outputstring = "";
    $outputstring .= "Hello, there";
    $outputstring;
  }
Do not make the mistake of trying to use print from within a template, as in this case:
{
    my $outputstring = "";
    $outputstring .= "Hello, there";
    print $outputstring;
  }
The contents of $outputstring will indeed be sent to the user's browser thanks to print, but the text will be sent ahead of the rest of the template. In place of the Perl block containing this code, the Text::Template module will insert the result from print—the result will have a value of 1 when printing is successful. Setting strings, rather than printing them directly, is the norm in the case of templates, but takes some getting used to if you are an experienced Perl programmer.

______________________

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