www.gibmonks.com

  Previous section   Next section

Practical Programming in Tcl & Tk, Third Edition
By Brent B. Welch

Table of Contents
Chapter 18.  TclHttpd Web Server


Form Handlers

HTML forms and form-handling programs go together. The form is presented to the user on the client machine. The form handler runs on the server after the user fills out the form and presses the submit button. The form presents input widgets like radiobuttons, checkbuttons, selection lists, and text entry fields. Each of these widgets is assigned a name, and each widget gets a value based on the user's input. The form handler is a program that looks at the names and values from the form and computes the next page for the user to read.

CGI is a standard way to hook external programs to web servers for the purpose of processing form data. CGI has a special encoding for values so that they can be transported safely. The encoded data is either read from standard input or taken from the command line. The CGI program decodes the data, processes it, and writes a new HTML page on its standard output. Chapter 3 describes writing CGI scripts in Tcl.

TclHttpd provides alternatives to CGI that are more efficient because they are built right into the server. This eliminates the overhead that comes from running an external program to compute the page. Another advantage is that the Web server can maintain state between client requests in Tcl variables. If you use CGI, you must use some sort of database or file storage to maintain information between requests.

Application Direct Handlers

The server comes with several built-in form handlers that you can use with little effort. The /mail/forminfo URL will package up the query data and mail it to you. You use form fields to set various mail headers, and the rest of the data is packaged up into a Tcl-readable mail message. Example 18-10 shows a form that uses this handler. Other built-in handlers are described starting at page 266.

Example 18-10 Mail form results with /mail/forminfo.
<form action=/mail/forminfo method=post>
   <input type=hidden name=sendto value=mailreader@my.com>
   <input type=hidden name=subject value="Name and Address">
   <table>
      <tr><td>Name</td><td><input name=name></td></tr>
      <tr><td>Address</td><td><input name=addr1></td></tr>
      <tr><td> </td><td><input name=addr2></td></tr>
      <tr><td>City</td><td><input name=city></td></tr>
      <tr><td>State</td><td><input name=state></td></tr>
      <tr><td>Zip/Postal</td><td><input name=zip></td></tr>
      <tr><td>Country</td><td><input name=country></td></tr>
   </table>
</form>

The mail message sent by /mail/forminfo is shown in Example 18-11.

Example 18-11 Mail message sent by /mail/forminfo.
To: mailreader@my.com
Subject: Name and Address

data {
   name   {Joe Visitor}
   addr1  {Acme Company}
   addr2  {100 Main Street}
   city   {Mountain View}
   state  California
   zip    12345
   country   USA
}

It is easy to write a script that strips the headers, defines a data procedure, and uses eval to process the message body. Whenever you send data via e-mail, if you format it with Tcl list structure, you can process it quite easily. The basic structure of such a mail reader procedure is shown in Example 18-12:

Example 18-12 Processing mail sent by /mail/forminfo.
# Assume the mail message is on standard input

set X [read stdin]

# Strip off the mail headers, when end with a blank line
if {[regsub {.*?\n\ndata} $X {data} X] != 1} {
   error "Malformed mail message"
}
proc data {fields} {
   foreach {name value} $fields {
      # Do something
   }
}
# Process the message. For added security, you may want
# do this part in a safe interpreter.
eval $X

Template Form Handlers

The drawback of using application-direct URL form handlers is that you must modify their Tcl implementation to change the resulting page. Another approach is to use templates for the result page that embed a command that handles the form data. The Mail_FormInfo procedure, for example, mails form data. It takes no arguments. Instead, it looks in the query data for sendto and subject values, and if they are present, it sends the rest of the data in an e-mail. It returns an HTML comment that flags that mail was sent.

When you use templates to process form data, you need to turn off result caching because the server must process the template each time the form is submitted. To turn off caching, embed the Doc_Dynamic command into your form handler pages, or set the page(dynamic) variable to 1. Alternatively, you can simply post directly to the file.tml page instead of to the file.html page.

Self Posting Forms

This section illustrates a self-posting form. This is a form on a page that posts the form data to back to the same page. The page embeds a Tcl command to check its own form data. Once the data is correct, the page triggers a redirect to the next page in the flow. This is a powerful technique that I use to create complex page flows using templates. Of course, you need to save the form data at each step. You can put the data in Tcl variables, use the data to control your application, or store it into a database. TclHttpd comes with a Session module, which is one way to manage this information. For details you should scan the session.tcl file in the distribution.

Example 18-13 shows the Form_Simple procedure that generates a simple self-checking form. Its arguments are a unique ID for the form, a description of the form fields, and the URL of the next page in the flow. The field description is a list with three elements for each field: a required flag, a form element name, and a label to display with the form element. You can see this structure in the template shown in Example 18-14 on page 260. The procedure does two things at once. It computes the HTML form, and it also checks if the required fields are present. It uses some procedures from the form module to generate form elements that retain values from the previous page. If all the required fields are present, it discards the HTML, saves the data, and triggers a redirect by calling Doc_Redirect.

Example 18-13 A self-checking form procedure.
proc Form_Simple {id fields nextpage} {
   global page
   if {![form::empty formid]} {
      # Incoming form values, check them
      set check 1
   } else {
      # First time through the page
      set check 0
   }
   set html "<!-- Self-posting. Next page is $nextpage -->\n"
   append html "<form action=\"$page(url)\" method=post>\n"
   append html "<input type=hidden name=formid value=$id>\n"
   append html "<table border=1>\n"
   foreach {required key label} $fields {
      append html "<tr><td>"
      if {$check && $required && [form::empty $key]} {
         lappend missing $label
         append html "<font color=red>*</font>"
      }
      append html "</td><td>$label</td>\n"
      append html "<td><input [form::value $key]></td>\n"
      append html "</tr>\n"
   }
   append html "</table>\n"
   if {$check} {
      if {![info exist missing]} {
         # No missing fields, so advance to the next page.
         # In practice, you must save the existing fields
         # at this point before redirecting to the next page.

         Doc_Redirect $nextpage
      } else {
         set msg "<font color=red>Please fill in "
         append msg [join $missing ", "]
         append msg "</font>"
         set html <p>$msg\n$html
      }
   }
   append html "<input type=submit>\n</form>\n"
   return $html
}

Example 18-14 shows a page template that calls Form_Simple with the required field description.

Example 18-14 A page with a self-checking form.
<html><head>
   <title>Name and Address Form</title>
</head>
<body bgcolor=white text=black>
   <h1>Name and Address</h1>
   Please enter your name and address.
   [myform::simple nameaddr {
      1 name    "Name"
      1 addr1   "Address"
      0 addr2"  "Address"
      1 city    "City"
      0 state   "State"
      1 zip     "Zip Code"
      0 country "Country"
   } nameok.html]
</body></html>

The form package

TclHttpd comes with a form package (form.tcl) that is designed to support self-posting forms. The Form_Simple procedure uses form::empty to test if particular form values are present in the query data. For example, it tests to see whether the formid field is present so that the procedure knows whether or not to check for the rest of the fields. The form::value procedure is useful for constructing form elements on self-posting form pages. It returns:

name="name" value="value"

The value is the value of form element name based on incoming query data, or just the empty string if the query value for name is undefined. As a result, the form can post to itself and retain values from the previous version of the page. It is used like this:

<input type=text [form::value name]>

The form::checkvalue and form::radiovalue procedures are similar to form::value but designed for checkbuttons and radio buttons. The form::select procedure formats a selection list and highlights the selected values. The form::data procedure simply returns the value of a given form element. These are summarized in Table 18-6 on page 264.


      Previous section   Next section
    Top