Remote Data Entry
In Session Four we will begin working with the methods (or functions) contained in WebClass.cc. This custom class is the foundation of any web application. We begin the Session with a discussion of the WebClass library. It will be followed with some examples of remote data entry application and we will discuss how to debug web applets.
The foundation of a dBASE CGI applet is the custom class called WebClass.cc. This class is derived from the Associative Array class. It's very versatile and offers significant power for developing web applications, much of which I will explore in the remaining Sessions of this Tutorial. In the following sections we will explore the structure of a web application.
The Basic Template
All my web applications use the same basic template. The code below is the template I use. It is derived from the program file created by the dBASE Plus Web Wizards. This simple program file highlights the main parts of the web application template:
1: 2: _app.errorAction = 3 3: 4: Try // Error trap entire CGI applet. 5: 6: ////// Create new instance of the Web Class 7: Set proc to WebClass.cc additive 8: oCGI = new CGISession () 9: 10: ////// Connect to the Web Server (StdIn/StdOut) 11: oCGI.Connect() 12: 13: oCGI.StreamHeader("Greeting Page") 14: oCGI.StreamBody("Greetings from dBASE" ) 15: oCGI.StreamFooter() 16: 17: catch (exception e) 18: 19: oCGI.errorPage(e) 20: 21: endtry 22: 23: /// cleanup 24: oCGI = null 25: 26: ////// all done 27: Quit
The first thing to note about this program is that the entire application is enclosed in a Try/Catch/Endtry structure. You will find that using this technique is very helpful for catching program errors and redirecting them to your browser. A web application is executed by a web server and, consequently, there is no user interface. Thus the standard error message that pops up when you are working on a desktop application, will not necessarily show itself when you're working on a web application. By using the try/catch structure, however, most error messages will be redirected to the web browser.
Next lines 7 and 8 in the above code creates a new CGI object from WebClass.cc. This object is given the name oCGI and it is the reference used for calling the functions contained in WebClass.cc.
On line 11 the application calls oCGI.Connect(). This is a very important function and you should always call it (or its equivalent if you subclass WebClass.cc) immediately after the oCGI object is created. This function establishes a connection to the web Server via a StdIn port and captures the incoming data stream sent from the HTML form. Web browsers send their data in ANSI format that must be converted to dBASE OEM Format before it can be used. oCGI.Connect() does this conversion for you. The above program template does not take advantage of this captured data, but we will be using it later in this Session.
oCGI.Connect() also opens a StdOut port which is an outgoing data stream back to the your web server. It is important to bare in mind when developing for the web that each CGI application must return a response page. The browser is waiting for this response and will become confused if that page does not arrive in a timely fashion. This is something that may take awhile to get used to.
The next three lines in the program template (Lines 13-15) create the response page. WebClass.cc partitions a web page into three units. They are called respectively, Header, Body, and Footer. Calling each function sequentially, and passing a message to StreamBody(), will create a response page containing your message. Because oCGI.Connect() has opened the StdOut port, this page is sent to the web browser automatically.
The next lines (17-21) form the error catcher. If there is an error in your program, the error information is passed to errorPage() which, in turn, streams this information in your browser window. This is a very nice feature. web applications can be tricky to debug. Other web development tools, like Perl, send error messages to StdErr, i.e. the Standard Error port. The web server receives this information and writes it into an error log. The developer then must open the error log to view the error message. This can be cumbersome and time consuming, especially if one makes as many errors as I do. But dBASE Plus is able to trap errors and stream them to the browser. Very nice!
The final lines of Web Example One (24 and 27) null out the CGI object and quits the application.
Building and Running the exe
Now that the code has been written, your program must be built into an executable file. You can do this by creating a new project and building the exe the same way you would build a desktop application. What I do, however, is create a buildIt.prg with the following three lines:
compile myapp.prg compile webclass.cc build myapp.pro to myapp.exe WEB
If WebClass.cc is not in the current folder, you need add the source code alias or the full path to its location.
Finally you need to call this application from your web browser. Although there are different ways to accomplish this, I will start with the command line equivalent, called a GET Method in CGI. In your browser's location bar enter the full URL to the folder where your application is located and the name of applet:
A few comments about CGI applications
One of the more important pieces of a CGI application is the Name/Value pair. The above URL includes the full path to the application and the name of the application. Most web applications also involve the web browser sending data. Because CGI applications work with different web browsers and different web servers there are industry standards for sending data to a CGI application. When data is sent from a web browser, it is organized into what are called Name/Value pairs. These pairs might look something like this:
The “name” of the pair is on the left, the “value” is on the right, and the pairs are separated by the ampersand (&). All information in these name/value pairs is text, so if your application uses data in numeric, date or boolean formats, it's your job to convert it.
The preferred method of passing data from a web browser to a dBASE application is from within an HTML form. The following is a simple HTML form.
<HTML> <HEAD> <TITLE> dBulletin Example Two </TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <H3>dBulletin Example Two</H3> <FORM METHOD=POST ACTION="EXAMPLE2.exe" ENCTYPE="application/x-www-form-urlencoded"> <P>Enter your greeting: <P><INPUT TYPE="text" NAME="greeting" VALUE="Default Value"> <P><INPUT TYPE="submit" NAME="submit" VALUE="Click to Submit"> </FORM> </BODY> </HTML>
There are a few key features in the <FORM></FORM> that need to be considered. First, the METHOD property is set to POST. This is the preferred choice for sending HTML form data. Your other choice would be GET, but this method of sending form data has more limitations and should be avoided. Also, notice the ACTION property of the FORM tag. This is where you enter the URL and file name for your CGI program. This is essentially your internet command line.
Now consider the INPUT tag. The example form has two input tags, one is a text field and the other is a submit button. Notice that both INPUT tags have a NAME and VALUE property. Indeed all HTML form elements have a name/value pair. When the submit button is pushed, the tag names and the values that the user entered, are sent to your web application. For the above example the data that is sent to your CGI application looks like this:
Processing CGI data with dBASE Plus
Since an HTML page sends data to your dBASE application packaged in name/value pairs, you will need a program that can make this data useful. The code below is a program file that receives information from a web form (a message in this case) and returns that message to the browser in a response page.
Set talk OFF // turn off interactive mode Set century ON // Y2K Try // Error trap entire CGI applet. ////// Create new instance of the Web Class Set proc to WebClass.cc additive oCGI = new CGISession () ////// Connect to the Web Server (StdIn/StdOut) oCGI.Connect() if oCGI.isKey('greeting') cGreeting = oCGI['greeting'] endif oCGI.StreamHeader("Greeting Page") oCGI.StreamBody( '<H2>' + cGreeting + '</H2>' ) oCGI.StreamFooter() catch (exception e) oCGI.errorPage(e) endtry /// cleanup our mess oCGI = null ////// all done Quit
This example is essentially the same as first example above. The lines that are new or that are modified are marked in bold type. This application is expecting data from the HTML form. As you saw earlier, that form has an INPUT tag which looks like this:
<INPUT TYPE="text" NAME="greeting" VALUE="">
When this data is sent to your application, oCGI.Connect() decodes and loads this name/value pair into the oCGI array. Indeed, oCGI.Connect() will load all the name/value pairs passed to your application. This includes field data and any other name/value pair that are passed. For example, the name and value of the submit button is loaded. Also the names and values of any hidden field in your HTML form are loaded into the oCGI array. What makes this procedure so useful is that you can pass both field data and other forms of data. Moreover, any of the CGI pair can be retrieved from the array anywhere in your program. In the example above I retrieve the value of the greeting with the following three lines:
if oCGI.isKey('greeting') cGreeting = oCGI['greeting'] endif
Then the variable cGreeting is passed to oCGI.StreamBody() which sends the greeting back to the browser in the response page.
Remote Data Entry
Thus far I have discussed the basic parts of a web application using WebClass.cc, the methods used to send data from a web browser to your CGI application, and finally, the procedures whereby your web application receives remote data for processing. One of the more common uses of a web application is for remote users to enter data into a database. Below is another example which demonstrates how remote data entry works using WebClass.cc. This example application adds two elements to the previous examples. First a query object is added and, second, the function called loadFieldsFromArray() is used. This example application is very similar to the web application created by the dBASE Plus Remote Data Entry Web Wizard.
Set talk OFF // turn off interactive mode Set century ON // Y2K Try // Error trap entire CGI applet. ////// Create new instance of the Web Class Set proc to WebClass.cc additive oCGI = new CGISession () ////// Connect to the Web Server (StdIn/StdOut) oCGI.Connect() ////// Open Query or Data Module cTable = "USERS.DBF" q = new query() q.sql = [Select * from "] + cTable + ["] q.active = true q.rowset.indexName = "userID" if oCGI.isKey('userid') cUserid = oCGI['userid'] endif oCGI.loadFieldsFromArray(q.rowset.fields, true) /// Print a response page cMsg = "<h2>Your information has been added to the database.</h2>" oCGI.StreamHeader("Information added") oCGI.StreamBody( cMsg ) oCGI.StreamFooter() catch (exception e) oCGI.errorPage(e) endtry /// cleanup our mess oCGI = null ////// all done Quit
WebClass.cc includes three very useful database functions. One of these functions is loadFieldFromArray(). This function can be used to append or update field data in your tables with information passed by the CGI data stream. All three of the data functions are based on name matching. That is, the HTML NAME sent from the web page must be an identical match to the name of the field in your table, including the case. You will recall that Connect() stuffs all the name/value pairs into an associative array. The WebClass.cc data functions attempt to match the element names in the CGI array with the field names in your table. Elements in the CGI array that do not match a table field are skipped. Thus the CGI Associative Array can contain both table data and other name/value pairs, but only the data intended for the table is actually added.
loadFieldFromArray() works for one rowset at a time. The function's first argument is the rowset's field array. You can, however, call this function over and over, each time passing a different rowset. Be careful, however. If you run this function for more than one rowset, it will update each and every matching field. This could be a problem if any of your fields share the same name in more than one table. For this reason WebClass.cc includes loadDatamoduleFromArray() which loads data from the CGI array into the rowset.fields of a datamodule. This function does discriminate with regards to which rowset should be used to append the data. For this function to work properly, the NAMEs for your HTML name/value must include the rowset name. The syntax is RowSet*@FieldName and an INPUT tag in your HTML page will look something like this:
<Input type="text" size="15" name="USERS1*@lastname">
loadDatamoduleFromArray() will split this CGI name into a rowset name and a corresponding field name. This way the function can append the field data to the desired rowset. It must be noted however, that if you use the RowSet*@FieldName convention with loadFieldFromArray(), the CGI NAME string will never match a rowset field ("RowSet*@FieldName" <> "FieldName").
The third and final data function inverts the loadFieldFromArray() function. LoadArrayFromFields() loads the existing associative array with data from fields in a rowset. To use this function, you must first create the elements in the oCGI array. Then using the name-matching convention, loadArrayFromFields() will assign values to the elements.
In summary then, WebClass.cc makes remote data entry very easy. You simply need to pass your data from a web browser to your CGI application. In your application you create your query object (or objects) and then call loadFieldFromArray().
The example applications that I am discussing in this session are derived from a working application used to manage College courses. In that working application my remote data entry routine is a bit more complicated than the above example. The most important complication is a check for duplicate records. In fact, I use something a bit more like the following snippet of code. When a web user requests that data be appended to the file of users, I will first check to see if the UserID (which in my case is a Student's College ID number) is already in the table.
/// check to see if the ID is already on file if q.rowset.findkey(cUserid) /// If the ID is on file, send a response page stating that fact. cMsg = "<h2>Your User ID is already on file.</h2> cMsg += "<h2>Use the Edit User Account form to update information.</h2>" oCGI.StreamHeader("Duplicate ID") oCGI.StreamBody( cMsg ) oCGI.StreamFooter() else /// otherwise append the data to the table. oCGI.loadFieldsFromArray(q.rowset.fields, true) /// Print a response page cMsg = "<h2>Your information has been added to the class database.</h2> cMsg += "<h2>You may now enter the course web sites below.</h2>" oCGI.StreamHeader("Information added") oCGI.StreamBody( cMsg ) oCGI.StreamFooter() endif
The second task for Session Four is to work with debugging a web application. This task is not always trivial. It is sometimes very difficult to debug CGI apps. However, with a basic understanding of how CGI program work and a little practice, your errors can be resolved.
One of main hurdles to debugging a web application is getting the error message. When your web server is running as a service in the Windows operating system, it is logged in as an anonymous user, however, you are logged in with a different user account. Moreover, Windows services do not have access to the computer's console (or video output) so the normal error message box that you are accustom to when building a desk-top form are not viewable from a web application.
There are at least three ways to attack this problem. First, as we saw above, the WebClass library contains code that will catch an error message and redirect it to the web browser as a web page. This works well so long as the WebClass object has been created. If, however, the error occurs before that object is loaded, then the error trap does not work.
The second approach to trapping errors is to use the errorAction property which was introduced with dB2K.04. This is a property of the _app object and can be set at the very top of your program file or in your program's INI file. For a web application it is best to use the INI file because there are a few errors that can occur before the program begins executing.
The INI file must have the same file name as your web executable (although a different file extension). You can use any text editor to create a simple text file, add the lines below, and save it as MyApp.ini, where "MyApp" is the name of your web application.
[ErrorHandling] ErrorAction=3 ErrorLogFile=PLUSErr.log ErrorLogMaxSize=100 ErrorHtmFile=error.htm
A full list of the ErrorAction settings can be found in the online help. An errorAction of 3 tells the dBASE Plus runtime to send the error message as an HTML page, log the error in the error log file (PLUSErr.log with the setting above) and quite the application. Although one may not want these actions to take place when running a desktop form, they are particularly useful for a web application.
dBASE Plus will use a default HTML page if it can not find the file identified by the ErrorHtmlFile property. If you would like to customize the error message, you can create your own error.htm file (or use a different file name in the property). The following table shows the variables (tokens) that you can put anywhere on that page. The dBASE Plus runtime will replace the token with the appropriate data.
Token Description %d Date and Time of error %e Application EXE filename %s Source filename %p Name of procedure or function in which error occurred %l Source line number %c Location where error code is displayed %m Location where error message is displayed
The third approach to trapping errors is to run your web server as a console application rather then as a server. This can be done with Apache and with the Microsoft Personal Web Server (PWS), but it can not be done with the Microsoft Internet Information Server (IIS). The PWS is Microsoft's web server for the Windows95 family of operating systems (Win95, Win98, and ME). If you are running one of these systems and you are using the Microsoft web server, you have PWS running. This server can not run as a service; it is, thereby, always runs as a console application. Thus any error messages generated by a dBASE CGI will be sent to the console.
Apache can run as either a service or as a console application on a WindowsNT, Windows2000, and WindowsXP system. Normally, when you install Apache it is installed to run as a service. This means that you must first stop the Apache service on your computer. To do this, open the Services window from the system control panel. Locate Apache and click the stop button. Then use the Windows Explorer, navigate to the Apache Bin folder on your hard drive (C:\Apache2\Bin\), and double click the file named "Apache.exe". A command window will open with a blinking cursor. That's your Apache server running. Just leave it that way (or minimize the window). To shutdown the web server, click the Window's close button in the upper right corner (the one with the "x").
While your web server is running as a console application, error messages will be sent to the computer console and you will see the familiar dBASE message box (as distasteful as it might be at times).
Now that we have a general idea about how a data entry applet works, let's try to build a simple remote data entry program. Use the Customer table from the WebTutorial database. We will enter the Customer's First Name, Last Name and City into the table.
The first thing you will need to build for Exercise 4.1 is a web page with a FORM tag, three INPUT tags, and a Submit button. The "Name" property of the INPUT tags is very important. It must match the field name in the database table. Your HTML will look similar to the following.
<FORM METHOD=POST ACTION="/app/Exercise0401.exe"> <P>Remote Data Entry Exercise <P>First Name: <INPUT TYPE="text" NAME="FirstName"> <P>Last Name: <INPUT TYPE="text" NAME="LastName"> <P>City: <INPUT TYPE="text" NAME="City"> <P><INPUT TYPE="submit"> <INPUT TYPE="reset"> </FORM>
To create HTML pages you will need a HTML editor or design program. FrontPage is Microsoft's product, but there are others. DreamWeaver is also popular. There are also many text editors that recognize HTML tags. I use a shareware product called EditPlus for most of my page layout.
Another issues that you will need to deal with is learning HTML. Again dBASE has helped. In the KnowledgeBase you will find "Web Form Controls for dB2K Developers". This document is an excellent introduction to the parts of a web form. Another valuable resource is "HTML Tag Reference" by Netscape.
The following is an example of the HTML form that you are building:
The Customer table also has a character fields for State and Country. In a desktop application we can use a lookupSQL and let dBASE do the "data morphing", i.e. "NY" will display in a combobox as "New York". In a web form, however, we are limited to the HTML in the browser. Therefore, if you would like to add HTML comboboxes to your form you must use the SELECT tag. In the following example the SELECT tag contains the field name "StateID" and OPTION tag contains the value that will be submitted. However the full name will display on the HTML form.
<SELECT NAME="StateID"> <OPTION VALUE=" ">Please choose a state</OPTION> <OPTION VALUE="CA">California</OPTION <OPTION VALUE="IN">Indiana</OPTION> <OPTION VALUE="MI">Michigan</OPTION> <OPTION VALUE="NY">New York</OPTION> </SELECT>
The HTML form will pass data to a CGI application, so next we must build this application. I use a basic "WebApp" template to get started.
////// Nuwer's Template ////////// Set talk OFF // turn off interactive mode Set century ON // Y2K Try // Error trap entire CGI applet. ////// Create new instance of the Web Class Set proc to WebClass.cc additive oCGI = new CGISession () ////// Connect to the Web Server (StdIn/StdOut) oCGI.Connect() oCGI.StreamHeader() oCGI.StreamBody("Response") oCGI.StreamFooter() catch (exception e) oCGI.errorPage(e) endtry /// cleanup oCGI = null ////// all done Quit
For a data entry application we will need to make a database connection. In this exercise we are using the Customer table from the WebTutorial database. My data connection code looks like this:
db = new DATABASE() db.databaseName = "WEBTutorial" db.active = true q = new QUERY() q.database = db q.sql = "select * from customer.dbf" q.active = true
After the query is active, the data passed from the HTML form must be appended to the table. Use the loadFieldsFromArray method to do this.
Finally a response page must be sent back to the web browser. This step is required, and might take some getting used too. All CGI application must send a response page otherwise the browser will timeout waiting for the response and an error will result. A simple response page for this exercise might look like the following:
cMsg = oCGI['FirstName'] + " " + oCGI['LastName'] + ; " has been added to the database." oCGI.StreamHeader("Information added") oCGI.StreamBody( cMsg ) oCGI.StreamFooter()
The Signup sample that you installed in Session One contains an example of one way you can add data validation to your web program. We can do this in Exercise 4.1 by adding the following code before the call to loadFieldsFromArray().
aErrorList = new array() // create an array to store possible // errors //Validate required fields if empty(oCGI["FirstName"]) aErrorList.Add('First name is missing!') endif if empty(oCGI["LastName"]) aErrorList.Add('Last name is missing!') endif if empty(oCGI["City"]) aErrorList.Add('City is missing!') endif if aErrorList.size > 0 // if there is an error oCGI.sorryPage(aErrorList) // Send error page and quit. endif
You can find a complete copy of my program file for this exercise in the file named \Source\Exercise0401.prg.
The last step is to compile and build the application. You can create a new project with the project manager or you can use the command line. For the latter approach type the following lines:
compile Exercise0401.prg build WEB Exercise0401.pro to ..\app\Exercise0401.exe
Note that there is already a compiled copy of WebClass.cc (webclass.co) in the Source and the App folder.
Earlier in this Session we discussed some issues related to debugging web applications. Our focus there was on trapping runtime errors and streaming them so that they could be viewable. Software developers know that there is another kind of debugging problem that must be addressed. This is the type of problem related to errors in the coding logic. The program runs and terminates normally, but it doesn't behave as expected. To debug this type of problem it is useful to inspect specific elements of a program.
To do this we will add a few methods to the WebClass library. These methods will extend the functionality of the WebClass library and are contained in the file named WebClassEx.cc. This class is a subclass of the dBASE WebClass. As such it contains all the properties and methods of the base class. In addition, the extensions add functionality to the class. There are a variety of methods in WebClassEx, and they will be discussed and used in the forthcoming Sessions. The first methods to be discussed are those that can help with debugging a web application.
To use the WebClassEx library the first few lines of the program file needs to be modified. First, we need to "set procedure to" WebClassEx.cc rather than WebClass.cc. And, second we need to create an instance of CGISessionEx rather than CGISession. The alternate lines look like the following:
set procedure to WebClassEx.cc additive oCGI = new CGISessionEx()
The first method that is useful for debugging is ShowMessage(). Conceptually, this method is like using a message box in a desk top application. If your CGI application is causing errors you will need to isolate the line causing the problem. By moving the ShowMessage function up or down in your program file, you should be able to isolate the problem line. I usually start somewhere near the top of the code. If the message is streamed to your browser, the code preceding the function call is working. Then move the function down in your code, rebuild the exe and run the applet again.
ShowMessage can be called using the following line:
oCGI.ShowMessage( cMsg )
The method will display cMsg in the HTML response page. It can be used to test whether your program is executing at a specific place, (i.e. cMsg = "I am here") or to view the values of a variable (i.e. cMsg = cSearch or cMsg = str(q.rowset.count).
The second most common problem with CGI applications concerns the name/value pairs that are being sent from the browser. The associative array used to maintain the name/value pairs is case sensitive. dBASE programmers are not typically concerned with case, and can be "tripped-up" in a CGI application. Moreover, your program may be looking for a name/value pair that, for some unknown reason, is not in the oCGI array. Therefore, it is sometimes useful to be able to see what is in the oCGI array. ShowArrayList will display the entire contents of the name/values array (i.e. oCGI['KeyName']). Be sure to call this method after you have made the CGI connection (viz., after oCGI.connect())
Exercise 4.2 is an example of how to display the list of name/value pairs. Let's first create another HTML form. Unlike our previous forms, however, this form will contain many controls. Use your HTML editor to create a new form. The ACTION property should point to "/app/Exercise0402.exe". Next add a few Text objects (i.e. entryfields); some checkboxes and radiobuttons; a select object (i.e. combobox); and finally add one or two hidden fields in the form.
Here is an example of such a form.
This form is also contained in the file named: Exercise0402.htm
To build the executable for this exercise, start with the same web application template that you used in Exercise 4.1 above. Modify that file so that it uses the WebClassEx library. Next call the showArrayList() after the oCGI object makes the connection to the web server.
Try Set proc to WebClassEx.cc additive oCGI = new CGISessionEx() oCGI.Connect() oCGI.ShowArrayList() catch (exception e) oCGI.errorPage(e) endtry quit
Save this program file using the same name as you used in the ACTION property of the HTML form.
The last step is to compile and build the application.
compile Exercise0402.prg compile WebClassEx.cc build WEB Exercise0402.pro to ..\app\Exercise0402.exe copy file webClassex.co to ..\app\webClassex.co
If you open Exercise0402.htm in your web browser and submit to this CGI program, the response page will look similar to the following.
| The CGI Array contains the following:
Before moving to the next Session, you may find it useful to review the HTML form elements that can be placed on your forms. The following is a series of example forms that show how these elements can be used. (These examples are form the Netscape HTML Tag Reference manual.)
Button, Password, Hidden, and Submit Input Elements
Checkbox and Radio Button Input Elements
File Input Element
Image, Reset, and Text Input Elements