By Joe Byrne
One of the main issues with CGI programming is the fact that you often need to 'embed' the HTML code into your CGI application. When your program produces output, the results are normally part of the web page you are dealing with. While this is not necessarily a bad thing, it does make it far more difficult to create CGI applications that are portable to other web sites, and/or its often necessary to change the CGI code to match changes in the web site's design.
When I first started doing CGI applications, I would create the actual web page design in my favorite HTML editor, then grab the HTML code and clean it up to use inside my CGI application. With a good text editor, this wasn't too difficult, just replace all of the quotation marks with apostrophes, and put "WriteCGI " at the start of each line. Granted, this method worked well, but maintainability was a nightmare. Every time I needed to make a change to the web page, I needed to make corresponding changes to the CGI program. This isn't a huge issue for small sites, but it gets pretty difficult when dealing with multiple CGI applications and lots of web pages. In addition, each change to the CGI application brought with it the increased possibility of bugs.
There are a number of optional methods, but the one I'm using now seems, at least to me, to work best. Basically, I still create my web pages in an HTML editor (my preferred program these days is WYSIWYG Web Builder) only now I use these pages as a 'template'. Instead of embedding the actual HTML code into the CGI application, I use a text substitution method to flag where my CGI data needs to be placed in the HTML page. To illustrate the method, lets use a simple HTML form like this:
The CGI application is intended to let the user maintain their own contact information. The record might already exits, or they could be adding a new record. In either event, I've taken the above form in HTML format and saved it in my CGI folder on the web server (actually I put all of these template files into a separate sub folder for organization purposes, but you are free to do it anyway you think is best). Now, when the CGI program runs, I open this template file for INPUT and scan each line for one of the field 'flags', denoted by the %% marker (you can use any type of flag you wish). The code is something like this:
Before this loop is executed, I check the database to see if the customer already exists. I use a cookie to save the customer ID when they log in from another page. If the cookie exists, then I look up their contact information in my database and create the necessary variables (FirstName$, LastName$, etc), so when this form is displayed, all of their current information is shown in the appropriate fields. This method allows me to change the entire look of the HTML page and/or add/delete fields at will without massive changes to the CGI application itself.
You may have noticed that I used a couple of additional flags, !!city and !!state. These two form fields are comboboxes and require a little special treatment. In order to create the combobox and show the properly 'selected' item, its necessary to create the data list each time the page is displayed. Since you don't know ahead of time which item is 'selected', there is no shorter way to do it. Therefore, when I see the !! flag, I jump to a sub routine to build the <OPTION VALUE code for the HTML field. In this case, I have two. (1) a list of city names, and (2) a list of State Names. The code for the city list is below:
If I don't pass a specific city name, the subroutine uses my 'default' value. Then each city name is read from a static data set. I use a variable (x$) to indicate which city name should be 'selected'. Obviously, it will be null except for one value. The for-next loop then outputs the HTML code for each city name with the appropriate closing </OPTION> tag. In the body of the HTML template, I include the final </SELECT> statement just to make it easier.
When the user clicks the 'Save Changes' submit button, the values of each field are passed to the CGI application where they can be validated and finally stored in the database. This particular sample simply executes the same CGI application saving whatever changes the user makes along the way. At the top of the CGI application, you simply need to gather the values from each of the form fields. That looks something like this:
You'll notice that I'm using a slightly different syntax to collect the field values above. I created a number of MACROs to minimize some of the coding. The MACROs are:
'---HTTP Form Macros---
MACRO lCGIField(cgiField) = TRIM$(LCASE$(CGIPARAM(buffer,cgiField))) 'String form data - LowerCase
MACRO uCGIField(cgiField) = TRIM$(UCASE$(CGIPARAM(buffer,cgiField))) 'String form data - UPPERCase
MACRO sCGIField(cgiField) = TRIM$(CGIPARAM(buffer,cgiField)) 'String form data - Normal Case
MACRO nCGIField(cgiField) = VAL(CGIPARAM(Buffer,cgiField)) 'Numeric form data
This method just allows me to make sure the incoming data is collected in the method I need, such as all lower case (lCGIField) or numeric values (nCGIField). Once the data is collected, we can validate that everything is correct or display another page instructing the user to fix something that is missing or incorrectly entered. Since the template page contains all of our field flags, when we re-display this form, all of the valid values will be populated in the correct spot on the Web form.
One thing that might be nagging at you, is how does this page get displayed originally? Surely we don't want the user to see all the %%fieldname flags! The thing to remember, is whenever you create a hyperlink, the target of that link can be just about anything you need, including a CGI program. So in the page where this form belongs, I simply use the CGI application as the target. This can be done with an embedded SSI command: (<!--#exec cgi="contact_form.exe"-->) or as the result of a button or other hyperlink click. A third way is to use an iFrame and set the CGI application as the initial target of the frame.
If you are not familiar with iFrames, it's probably one of those things that deserves a bit of investigation. The iFrame is supported by most browsers these days. I tend to use features that are mostly supported across the largest segment of my intended audience, and not worry too much about those still using older browsers. To me, technology moves too quickly to stay in a rut so if the user's browser doesn't support iFrames, I give them a gentle message telling them that they can't use this feature of the web site unless they update their software. In my experience, I have yet to have a problem with this.
The iFrame is basically a 'web page within a web page'. The frame loads any external HTML page (or code) into the area defined by the iFrame control. When the page is first created, it can be blank, but most often you define a specific 'first page' to display. This can be a simple menu page or your CGI application! When the page in the iFrame changes, only the data in the frame is effected, the rest of the page remains as it. This makes it nice and easy to create a series of small forms within a larger web site. For my purposes here, I used my site's template page and created an iFrame where I want the contact form to be displayed. I set the starting value of my iFrame to be the CGI program (./cgi-bin/contact_form.exe) which runs the above program and displays the results in the iFrame area. If a cookie exists for this customer, the page (which appears as a single web page to the user) is populated with their existing data. If no cookie exists, the web form is displayed with empty fields ready for the user to fill out.
Whatever format you choose, the primary goal should be maintainability. CGI programming is no different than any other type. You want to be able to reuse as much of your code as possible, therefore, creating a method of separating the HTML code from the 'guts' of the CGI application is normally a good thing. This idea is not limited to just PB CGI programs, but those done in any other language such as perl or PHP. Template files can go a long way to make your code portable and easier to maintain.