An Introduction to Web Applications
Using dBASE Plus

Ken Mayer

Written for dBCon 2004,
Montreal, Canada

NOTE: This was written in 2004, does not take into account HTML 5, and there is little discussion of CSS. It is pretty basic, but may still be useful to help get you started with web application development. There have been a few very minor updates to this, including URLs at the end, and some notes for finding documentation.

SOURCE CODE: You can download the source code for this if you wish by clicking here: dBCon-WebApp.zip -- download this and unzip to a folder as shown below ...

Menu

This "paper" is fairly lengthy, so to skip to a specific section, just find it below and click on the link ...:

Introduction
     Testing Using Apache
     Web Application Mapping
     Deployment
A Basic Overview of a Web Application
HTML -- The Universal Language Used for Web Pages
dBASE Form Controls Compared to Web Form Controls
     Text
     Entryfield
     Password
     Checkbox
     Radiobutton
     Editor
     Combobox/Listbox
     Pushbutton
A Simple dBASE Web Application
     Folder Design And Layout
     dBASE Web Classes
     Creating the Application
     Starting Your Application
     View The Data
     The Editing Program
     Deleting a Row
     Adding New Data
     An HTML Menu/Front-End
     Pulling It All Together
Wrap-Up


Introduction

This paper assumes a few things, such as the developer having a basic working knowledge of dBASE applications. The paper also assumes that you have the most recent version of dBASE Plus, etc. Note that it is not really necessary to have the most recent version of dBASE, but it doesn't hurt.

What this paper will discuss is web applications, and specifically web applications using dBASE Plus. There are many things to consider, and this paper will be covering the basics -- a paper attempting to discuss everything would be lengthy indeed (and for the purpose of the Conference, would probably take at least all day, if not both days scheduled ...).

To understand all of this, this paper will start at the basics of web applications, HTML (basic language for web pages), will compare dBASE form controls to those available in HTML and the differences between them, create a simple application and show that the data can be modified easily through a web application, discuss some issues with servers, and some miscellaneous things that may be useful to know.

Note that the lecture at dBCon will, due to time constraints, not be able to cover everything discussed in this paper. The paper has more information, and the sample web application that is created should be documented well, and as useful as I can make it. Also note that even a simple web application is very different from a more standard dBASE application -- during the lecture at dBCon, we will not be able to finish the whole application.

Testing Using Apache:
NOTE: There is a huge amount of information on setting up Apache for use with web applications, and we don't have time to spend going over all of it here. There is information in the Knowledgebase ("Help" menu in dBASE itself, then select "Knowledgebase", select "Intermediate", and it's the first item in the list titled: "Setting Up Apache for dBASE Web Apps"). In addition, if you install Apache from the dBASE Plus CD, note that there is a "Setup Apache" option on the installer screen.

NOTE: The information above is pretty old, suggest that with dBASE Plus 11, you go to:
C:\Program Files (x86)\dBASE\Plus11\Docs
and find the file in that folder:
dBASE PLUS 11 and Apache 24x_Configuration Guide.pdf

Web Application Mapping:
If you examine the code we're using for this sample application, we are using what is called "Web Application Mapping" -- we are not using the standard ".exe" file extension for our application files, we are instead using ".dbw", which stands for "dBASE Web". This is discussed in detail the OLH (Online Help) for dBASE under the topic "Web Application" and "mapping" (a sub-topic). There are several things that need to be done, including telling your operating system what .dBW means.

Deployment:
Deploying your application to a web server is a topic that cannot be covered in this paper, simply because it is too complex. There was an article written by another user on the basics of setting up IIS for use with a dBASE application, but I cannot find it. There are rights issues that must be dealt with, and more. While I have written some web applications in dBASE, including the bug tracking system that is/was used by dBASE, Inc., I cannot tell you all the details of what was needed for the actual deployment, as that was done by the web minister for the company.

Back to the Menu


A Basic Overview of a Web Application

What is a web application? It is, simply put, a way of interacting with data over the internet or an intranet (an internal version of the internet within a company ...). Seems pretty basic, and to one extent, it is. Some examples: A shopping cart (see www.amazon.com for a very good version of this); a Blog (web log); a message board; and a lot more.

To fill in some details, a web application is a series of web pages using HTML, and sometimes other technologies such as JavaScript, Java, ASP (a MicroSoft technology), and many other options, to allow an end user (or users) to view data, sometimes add, edit, delete data, view reports, etc.

All that makes a web application different from any other, is that it uses HTML and various HTML extensions and add-ins to do the same things that any other application. Just like any application, they can interact with data, or they can be completely data independent. Note that when I state that HTML applications can interact with data, that (as we'll see) HTML form controls do not know what a table is, what a row is, or what a field is ... we'll have to do that in dBL code.

There is a lot more information on how web applications work in the help file found when dBASE is installed, at this location:

   C:\Program Files\dBASE\PLUS\Web\WEBHELP.HLP
(This assumes a default installation of dBASE Plus ... if you installed to a different location, the folder "Web" should still be there ...)
NOTE: The information above is pretty old, the file does not exist in current versions of dBASE. However, in the
C:\Program Files (x86)\dBASE\Plus11\Docs
folder, you can find the file:
web-examples-overview.pdf
which may be useful.

This help file explains the subject in the topic "How Do Web Applications Work?" better than I can ...

Back to the Menu


HTML -- The Universal Language Used for Web Pages

HTML, as noted earlier, is the universal language used for web applications. HTML stands for HyperText Markup Language. It's been around for awhile, and is constantly evolving as needs evolve ... HTML has had many additions to it over the years including CSS (Cascading Style Sheets), XML (Extensible Markup Language), DHTML (Dynamic HTML) and more. However, it all boils down to the basics -- HTML. The following is a very short tutorial on HTML.

Basics:
First the basics: HTML is text. Simply, succinctly, it is text. Everything that can be done with HTML can be done in any text editor, such as NOTEPAD, WORDPAD, the dBASE Source Editor, etc.

There are some things you need to be aware of that can be frustrating to a first time user of HTML, and that's what this mini-tutorial is about. Most of this will not be covered in the presentation at DBCon, due to time constraints.

Line Breaks and Paragraphs:
HTML automatically wraps text. If you enter text in the following manner into any editor, HTML will assume it is all one paragraph:

   This is a line
   and a second line
   and a third
   and one more

   after a blank line

HTML will convert this to:

   This is a line and a second line and a third and one more after a blank line

If you really want the text to appear as entered in the first set, you will need to insert some specific tags used to handle line breaks, and paragraphs. The difference between a line break and a paragraph tag is that one inserts a carriage return, the other inserts a carriage return and a line feed.

   This is a line<br>
   and a second line<br>
   and a third<br>
   and one more
   <p>
   after a blank line

In the discussion on tags below you will note that the paragraph tag can use an ending tag (</p>), but it is not 100% required, and there are places where it's easier to just use the starting tag without the ending tag.

Spaces:
HTML also assumes and displays one space between words. If you want more, you have to use what are called "entities". Entities are specific codes that HTML interprets before displaying. Any good HTML book will have many of these, but you should know about the non-breaking space:

   &nbsp;

The non-breaking space can be used to force spaces, such as at the beginning of a paragraph, where you might want to simulate a tab:

   &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;This paragraph has an indent at the beginning.

Angle Brackets
As you can see, angle brackets are used in HTML a lot. As a matter of fact, they are so important to HTML that if you use them in your text, they can cause problems -- if you had text like:

   This is a <test> of angle brackets

What would be displayed in the browser is:

   This is a  of angle brackets

Why did the browser do that? It encountered what it sees as an HTML (or XML) tag, and it doesn't know what it means. So it just doesn't show it.

A single bracket is sometimes interpreted properly by your browser, however, you will find that different browsers sometimes interpret things differently. You should instead convert any angle brackets to the HTML entities:

<     &lt;
>     &gt;

Ampersands
Ampersands get treated specially as well, and again some browsers handle these differently. The ampersand (&) character is used to begin an HTML entity (as shown above), so in order to be sure that your browser sees an ampersand the way you intend it to, you should use the HTML entity:

&amp;

This tells the browser to display an ampersand.

Any good HTML text will give a listing of standard entities. There are a lot, however, so many books will not list all of them.

Formatting Tags:
HTML uses what are called tags to define formatting and special parts of a page. Most tags have a "begin" tag and an "end" tag. An example is the tags used in the first sentence of this paragraph to make the word "tags" appear in italics. This actually looks like:

<i>tags</i>

The first tag (<i>) tells the web browser to "begin" italics -- all text that follows that tag will be italicized. The second tag (</i>) tells the web browser to "end" italics. By using this pair of tags, all text between them will be italicized.

There are many tags, and we won't be covering all of them here. Many tags make sense and are pretty basic -- the following are a few of the basic formatting tags that can be used in HTML -- note that without becoming a complete primer on HTML, we can only cover a small amount ...:

BeginEndStands forLooks like
<b></b>BoldfaceBold Text
<i></i>ItalicItalic Text
<u></u>UnderlineUnderline Text
<h1></h1>Header Size 1

A Header

<h2></h2>Header Size 2

A Header

<h6></h6>Header Size 6
A Header
<hr>(none)Horizontal Rule
<p></p>Paragraph(begin paragraph with blank row,
note that the end paragraph tag
is not always required, but is
probably a good habit to be in ...)
<br>(none)Line BReak(begin new line)
<center></center>CenterCenters text on the page
<table></table>TableYou're looking at a table.
Note that there are many tags
involved in working with tables,
we will look at some when we
start designing our sample web
application ...
<font></font>FontMANY attributes and options
can be used with this, including
color and size, as well as defining
specific fonts to be used. More as
we start defining our application.

The tags shown above are really basic. Some of these (and many other tags, some of which we will discuss as we work on a web application below) have what are called 'attributes', which are name/value pairs that are used to modify how something works. For example, the table tag used to display the table above, used the attribute: border="1", which tells the browser interpreting the HTML that the table should have a border of size "1" (not using it, the border defaults to a size of zero -- meaning no border).

At this point, to really understand HTML, I suggest that you:

Back to the Menu


dBASE Form Controls Compared to Web Form Controls

Note that as with the HTML discussion above, most of this will not be covered during the DBCon presentation of this paper.

The basic interface used on a form in a dBASE application is the entryfield. In addition to this, we have a variety of other useful items, such as the editor, the combobox, the listbox, the radiobutton, and the checkbox. We also have pushbuttons.

There are other objects built into dBASE that we won't be discussing here, as they are not native to HTML forms. The ones mentioned above have equivalents in HTML Form tags, and we will look at the basic options for these here.

In order to specify part of an HTML page as a form, we must use special tags (surprised?), specifically: <form>. At the most basic, this will create a place that the user can interact with controls, but the user cannot send data through the web server back to your own code. There are attributes that can be used to specify how to send the data back to the server, and what program to run when the data is received. As we build an actual web application we will discuss this. Interestingly, HTML allows multiple forms in one web page, which is useful for the discussion below (and could be useful for other purposes, but we won't go into that in this paper).

Text:
Text in a dBASE form is created by using a text or textLabel object. In HTML text is just text. Anything that is not in a tag, or defining a tag, is text. That's pretty basic, eh?


Entryfield:
This is where things start to get more interesting. In dBASE, the entryfield is called an entryfield. In HTML, it is called text! However, we know this because we are using the form element called input, and the "type" is "text".

A simple entryfield in HTML is defined in the following manner:

   <input type="text" name="test">

What this looks like in an HTML form is:

This doesn't look like much, but it is a basic entryfield, pretty much as if you had placed an entryfield on a form in the form designer in dBASE.

If you want text to the left of this, you would simply type that text into your HTML page:

Enter some value:

Attributes for this are very much like properties in a dBASE object. You can include a variety of attributes, including, but not limited to:

attributewhat it does
nameGives a reference to the value when a form is submitted
sizeSpecifies the width of the input field in number of characters
maxlengthSpecifies the maximum number of characters the user can enter
valueSpecifies the beginning value that is displayed

For the purposes of an HTML form, the required attributes for an entryfield are: name. That's it. You have to name the entryfield (the same for all other objects, we'll talk about each as we get there).

The value attribute, when it comes to creating a web application, can be used to assign the current value in a field in a table to the entryfield. This is useful for data editing ...

Password: This is a special type of input control. It works just like an entryfield, but obscures the value by masking it with special characters as defined by your browser. In dBASE you have to do some extra work to do this, in HTML, it is built in ... To use a password control:

      <input type="password" name="testpassword" value="SomePass">
   

What this looks like in an HTML form is:

Enter your password:


Checkbox:
The checkbox is used for logical values - true or false. In dBASE (using level 7 tables) a logical field can be true, false or null. In HTML it's more simple -- either true or false, there is no 'null' option.

The thing about Checkboxes in HTML is that when a form is submitted, if the checkbox is checked, a value is sent back to the web server; if the checkbox is not checked, then no value is returned. We will examine this when we get to the sample application. It can be a bit confusing.

Checkboxes are defined using the INPUT tag in HTML as is the "entryfield" tag. The checkbox has the following attributes:

attributewhat it does
nameGives a reference to the value when a form is submitted
valueIf checked, what is passed back to the server?
checkedSpecifies if the checkbox starts out checked when the form is displayed

A basic checkbox in HTML will look like:

   <input type="checkbox" name="MyCheckbox" value="Check1">

Note that there is no place to enter text that is displayed with the checkbox. This is because you just add the text yourself, and you can add it before or after the checkbox.

A simple form with a couple of checkbox options might look like (in source):

Select your preferences:<br>
Chocolate:  <input type="checkbox" name="ChocolateCheck" value="Chocolate" checked><br>
Vanilla:    <input type="checkbox" name="VanillaCheck" value="Vanilla"><br>
Strawberry: <input type="checkbox" name="StrawberryCheck" value="Strawberry">
And in HTML this would look like:

Select your preferences:
Chocolate:
Vanilla:
Strawberry:

That's not a great layout, is it? The text is lined up, but the checkboxes are not ... (one option would be to remove the <br> tags, and all the checkboxes would be on one line ...) We'll come back to layout a bit more, but a hint: think "table" ....


Radiobuttons:
Radiobuttons are used to define a list of options that can only have one value returned. The definitions for Radiobuttons are very similar to checkboxes, however one very important thing: you group radiobuttons by the name attribute. That is how HTML knows to deal with the choices, and how to return a value.

A simple definition of a radiobutton in HTML would be:

   <input type="radio" name="MyRadio" value="RadioChoice1">

Using the same options that we used for the checkbox shown above, but changing to radiobuttons, the HTML would like like:

Select your preferences:<br>
Chocolate:  <input type="radio" name="FlavorRadio" value="Chocolate" checked><br>
Vanilla:    <input type="radio" name="FlavorRadio" value="Vanilla"><br>
Strawberry: <input type="radio" name="FlavorRadio" value="Strawberry">
And in HTML this would look like:

Select your preferences:
Chocolate:
Vanilla:
Strawberry:

Note that the layout issues are the same as with checkboxes.


Editor:
The editor control in dBASE can handle large amounts of text. In HTML the equivalent is called a textarea.

The textarea HTML element works differently than the input element. Among other things, it requires an "end" tag, and the text is placed between the beginning and ending tags -- there is no "value" property.

Basic attributes of the textarea element are:

attributewhat it does
nameGives a reference to the value when a form is submitted
rowsSpecifies the height in number of rows
colsSpecifies the maximum width (in characters)

A simple textarea element would be defined as:

   Notes:
<textarea name="MyEditor" cols=50 rows=3> Enter some text here ... </textarea>

In an HTML form it would look like:

Notes:

The scrollbars would appear only as needed, or might appear depending on how your browser interprets HTML.


Combobox/Listbox:
The combobox and listbox can be defined in HTML forms using the select form element. For the select element you must define each item you wish to appear in the list. This is done using the option form element. You must always use an end tag for the select element.

Basic attributes of the select element are:

attributewhat it does
nameGives a reference to the value selected when a form is submitted
sizeSpecifies the number of items to show -- if this is omitted
or set to 1, a combobox is shown. If set to two or higher,
it works like a listbox. An interesting option for the listbox
is if you specify the size to a value greater than the
number of elements in the list, a "nothing" option is added.
multipleSpecifies whether multiple options may be selected (user
would hold the Ctrl key while clicking ...)

For a combobox, you would define your select element this way:

   Select a flavor: 
   <select name="FlavorCombobox">
   <option selected value="Chocolate"<Chocolate
   <option value="Vanilla"<Vanilla
   <option value="Strawberry"<Strawberry
   </select>

In an HTML form this would look like:

Select a flavor:

To change this to a listbox, you would specify either the multiple attribute, or a height (set the size attribute to a value ...):

   Select a flavor: 
   <select name="FlavorListbox" size="3">
   <option selected value="Chocolate"<Chocolate
   <option value="Vanilla"<Vanilla
   <option value="Strawberry"<Strawberry
   </select>

In an HTML form this would look like:

Select a flavor:

Note the location of the text for this. Again, think "table" ... we'll look at that when we get to a real application.


Pushbuttons:
HTML forms come with two default pushbuttons, and if you want to learn about JavaScript, you can do even more with them. For our purposes we will simply deal with the basic buttons that are part of HTML.

The two options for pushbuttons are created using the input element, and are reset and submit. The reset button clears out the elements on a form, resets default values, etc. The submit button actually will submit the form to the web server, passing the values to the application specified in the form tag (we haven't looked at that yet ...).

To code a reset button in HTML you have one attribute that is optional, which is "value" -- this is the text that appears on the button. If you leave it off, the default value of "Reset" will be used.

Note: It is possible to do a lot more with Pushbuttons than what is shown here, but for that you need to learn to use JavaScript, which then allows you to program your pushbuttons ...

   <input type="reset"><br>
   <input type="reset" value="Clear the Form">

In a form, this would look like:


A submit button looks the same, except for the 'type':

   <input type="submit"><br>
   <input type="submit" value="Send It In">

In a form, this would look like:



Now that you have the basics for creating form elements in HTML, we need to consider actually creating an application ...

Back to the Menu


A Simple dBASE Web Application

At this point we are going to go through the steps of creating a very basic dBASE/Web application. Once again, this assumes you have some knowledge of dBASE coding, and there are things that will not be explained in a lot of detail.

dBASE uses CGI (a standard web technology) for web applications, but it uses a trick -- it passes and receives information using the dBASE File object, and streams the data through the StdIn and StdOut ports of the operating system. This means that it is creating virtual web pages that are streamed directly through the web server, and reading the data straight from the web server, without ever having to write it anywhere else first.

We will be using a custom class that ships with dBASE which will do some of the work for us.

For our application we will assume that we need a few specific capabilities: View the current data; add new data; edit current data; and delete data. We could add reporting options, and we could do a lot more, but we're going to keep this simple.

I have found that most of the time it is useful to have a front-end HTML page that only changes if I need it to. This is called a static page. The pages that are generated by dBASE code are what are called "virtual" pages -- they are created on the fly, and are passed directly through the server to the web browser -- the actual HTML page does not exist anywhere on the server. That is one of the great strengths of using dBASE to create web applications -- it can stream the HTML directly to the server and the end-user. This is very fast!

Let's start with a basic application definition. Let us assume that you are creating a way for sales representatives to store customer information. We won't worry about the actual sales end of things, that would be a much more complex application, and we're just learning right now.

You could create a login screen, but most web servers handle authentication of that nature. We won't deal with that. Let's assume that your sales representatives already have access to the server, and because your server is set up with appropriate security, only someone with a userid and password can get in. So security is not our concern ...

Once your sales rep logs in, you will want them to view a specific HTML page that might be considered to be a menu for your application. You would need to have options for each program -- although as you'll see, some of this has been simplified into just a couple of options:

View the Data
Add New Data
Edit Data
Delete Data

And of course you could set it up to view the data in reports based on zip (postal) codes, age groups or other demographics, or what-have-you. We are once again doing simple here.

The sample application already has a table defined, with an attempt to use some of the various interface controls mentioned in the previous section. As we define this application, we will look at all of this.


Folder Design And Layout
When it comes to design, there are many ways to do things. A lot might depend on the network guru at your firm, for example. He/she may have a very specific design in mind, based on security concerns, for example.

Once again, we're keeping this simple for the purposes of this discussion, but note that the best thing about a good design is that it doesn't really matter where your tables are (use a BDE Alias); or where the code is in relation to them.

For this example, note that the menu will be in the "root" folder, the Source Code will be in its own folder, the compiled application will be in yet another folder, and finally the data will be in a folder of its own.

This gives a layout like:

   dBCon-WebApp
      | 
      +--- SourceCode
      |
      +--- Executables
      |
      +--- Tables

Each of those is a folder. In the dBCon-WebApp folder is the main menu (which we will discuss later), the "SourceCode" folder is where we will be developing our application, the "Executables" folder is where will we put the compiled application, and the "Tables" folder is where the data will reside. For this application to work properly, we are using a BDE Alias which points to the folder with the tables. This allows us to move the tables if needed and not change anything in our application code.


dBASE Web Classes
dBASE Plus ships with a custom control that can (and should) be used with web applications. It takes care of a lot of the work that you might have to do otherwise. You can examine the web classes by opening the file:

   modi comm :webwizards:WebClass.cc

It is not a good idea to modify the base class. However, you can create your own subclass of this easily. You may want to close the source editor, just to be safe ...

For our purposes, we're going to assume a company called "Golden Stag Productions", and so we will see names that reflect this in various places.

For example, from the Command Window:

   modi comm GS_WebClass.cc

For the example I have created a subclass of the Webclass itself, which will allow me to add my own code, override anything I might wish to, and leave the rest "as is".

The class used here includes some new methods:
streamDetail() -- this literally is just a replacement for the dBL command:

     oCGI.fOut.puts( SomeString )
Is this:
     oCGI.streamDetail( SomeString )
Any better syntax-wise than the previous command? Not really, but it adds a bit of consistency when you use a lot of the other methods of the WebClass.

For the most part, we will be using a different technique than streamDetail() to put output together to send to the browser. There are reasons why it might be useful to send output directly across, one command at a time, but there is one drawback: If you use the default errorPage() or sorryPage() methods of the CGI object, your output can get very odd. If you have already started streaming output to the browser, and then an error occurs, it can be interrupted and then the output from the errorPage or sorryPage methods will be displayed, and these screens will not look very professional.

Bowen Moursund (I believe) came up with a better technique, which is to build an output string, and send the whole thing at one time to the browser. To facilitate this, the GS web class uses a string: oCGI.cOutString, and on instantiation defaults it to a null value. During the processing, we add to the string (you will see this in the code samples), and if no error occurs, we will output the whole string. We can get away with this because dBASE's strings can be very large (the only known limitation is the amount of memory available on the computer ...). If an error occurs, or we have a need to use the sorryPage method, we don't have to worry about a poor-looking display.

streamTitle() -- this streams out the title at the top of the page

streamFormBegin() -- this streams out the form tag, with the appropriate method and such, including some necessary attributes. All you have to do is pass it the name of the program to execute when a form is submitted. (We'll get to it in detail later).

StreamCustomerScreen() -- this streams out the HTML tags (and data) for the Customer screen. By making this a part of the class, it allows us to call it without some potential security risks.

There is an overridden method:
streamFooter() -- this changes the text streamed out at the bottom of the HTML page, and adds a "Powered by dBASE" image.

(Note: This class is actually a modification of code found in the dUFLP file kenWebClass.cc, only much simplified ... there are methods in that class to stream out the individual form controls, and much more.)

Yes, But What Does It Do?
After all that, what exactly does all this code do? Briefly, the CGISession class which is found in :WebWizards:WebClass.cc, is the main interface for web applications for dBASE. It handles communicating with the server, and passing/receiving data to/from the server.

If you closely examine the code for WebClass.cc, you will find that CGISession is a subclass of an associative array. However, the methods of the class, such as Connect(), actually create connections to the web server, using the StdIn and StdOut (Windows API) capabilities of the web server. Some other methods which we will be using store information in the associative array, allowing us to send data out to the server and to read the data that is returned from the server.

Back to the Menu


Creating the Application
In order to create a web application, there are some basic concepts that you need to consider, besides any design and appearance issues.

First, you should always use error trapping in every program you use on the web. The reason? With the exception of Apache (and maybe some others), most web servers don't show dBASE error dialogs. Even if you are using Apache, is someone going to be monitoring the web server itself for errors? If you trap your errors using the dBL try/catch/endtry dialog, you can display an error message for the user, asking them to contact the web minister, and you do not hang the server ("hanging" the server means that it is locked up, waiting for a user response ...).

Second, try to remember that dBL is object-oriented, and that even though the code you are creating is more procedural than it is in the Windows environment, OOP is still a good and useful thing.


Starting Your Application
Most of my web applications start the same way:

   // open the procedure file:
   set procedure to GS_WebClass.cc additive
   // create an instance of the class:
   oCGI = new GS_WebClass()
   // Set Up the connection for StdIn/StdOut ...
   oCGI.Connect()
   // Set the web master address, used in the error code
   oCGI.setWebMasterAddress("kmayer@dbase.com")

Depending on what needs to be done from there, the code tends to be placed inside a big wrapper try/catch/endtry, and sometimes there are smaller bits of code also wrapped in them.

For example, the next thing that needs to be done is to open the database, and so on. For this I tend to wrap just that code in a try/endtry block, and use the webclass errorPage if an error occurs:

   try
      // open the database:
      dCustomer = new Database()
      dCustomer.databaseName := "dBCon-WebApp"
      dCustomer.active       := true

      // open the table:
      qCustomer = new query()
      qCustomer.database     := dCustomer
      qCustomer.sql          := "select * from Customers"
      qCustomer.active       := true
      // create some shortcuts:
      rCustomer = qCustomer.rowset
      fCustomer = rCustomer.fields
      // set the index:
      rCustomer.indexName    := "LastFirstNames"

   catch( Exception E )
 
      oCGI.errorPage( e )

   endtry

From here of course, the code will all change. However, knowing that it all starts the same is useful. We could, for example, store all that in a program file, and then use a pre-processor directive to INCLUDE the file in all of our programs. That way any changes could be made in one place. We're going for simple, after all ...

Back to the Menu


View The Data
This code may seem like it should be simple, and to an extent, it should be. We're not actually creating an HTML form, for example. We're going to create what is called a "drill down" report. What this means is that it's a list of data with the option to "drill down" to the full record.

What we're going to do here is display part of the customer data -- customer number, name, city, state and postal codes. If a sales rep then wants to view more data (allowing them, in this case, to edit it!) we need to give them a place to "click" and bring up more data.

The place to "click" uses an HTML tag type that we haven't discussed yet:

ANCHOR TAGS
One HTML tag (of many) we haven't looked at is called an anchor -- it is used to provide links either to other web pages, or to programs, websites, email addresses, and more.

The basic syntax for the anchor tag is:

   <a href="what you need to happen">Some Text</a>

For example, to place a link to, say, my personal dBASE website from here:

   <a href="http://www.goldenstag.net/dbase" target=_blank>Goldenstag dBASE Page</a>

which would look like:

Goldenstag dBASE Page

Okay. Now we have discussed the anchor tag ...

This should be pretty simple, except that in order to get our data to display in a manner that is easy to read (columns lining up, etc.), we are also going to work with HTML Table tags. We will discuss each as we go.

What we are going to be doing is quite literally simply streaming a lot of HTML tags out to the end-user's browser. There is no interim or temporary HTML file involved -- we never store the HTML tags anywhere on the server, except in the dBL program that creates the HTML output.

The basic program should always stream out the beginning tags for our HTML, some of which was discussed earlier, and is in the file GSCustomerStartCode.prg.

When outputting data using HTML tables, there are a variety of things to consider. For example, do you want a border around the cells in the table? What kind of formatting do you want? etc. We are going to keep things as simple as we can, but ...

Table Tags:
There are several HTML tags used when working with tables. The first is to start the table (and don't forget to end it):

   <table>
Everything after the 'begin' table tag until we reach the 'end' table tag should be part of the table. While this is not necessary, as some browsers will assume that if the correct tags are missing that the information should be displayed elsewhere (in Netscape what usually happens is that if tags are missing, the information is displayed BEFORE the table -- this can be a bit confusing, but there you go ...), you should think about your design, and try to be careful.

The next table tag that needs to be considered is the Table Row tag, which has an end tag as well:

   <tr>

If you want to, you can create table headings using the Table Header tag (by default the table header text is bold and centered in the column ...):

   <th>

And finally we get to the most important part, that which defines the individual cells of the table (or in dBASE consider the individual fields for the row), the Table Data tag:

   <td>

Each of these tags have attributes, some of which we may use for this simple display, some of which we probably won't.

Start The Output ...:
The following is the beginning of the code being used in the demonstration program.

   // start the table:
   oCGI.cOutString += [<center><table border="1">]
   //
   // Stream out some headings for the columns:
   oCGI.cOutString += [<tr valign="bottom">]
   oCGI.cOutString += [<th>Action</th>]
   oCGI.cOutString += [<th>Customer<br>Number</th>]
   oCGI.cOutString += [<th>Customer<br>Name</th>]
   oCGI.cOutString += [<th>City</th>]
   oCGI.cOutString += [<th>Postal<br>Code</th>]
   oCGI.cOutString += [</tr>]

   // Move to the first row in the rowset:
   rCustomer.first()

   // loop through the table, outputting the data we need:
   do while not rCustomer.endOfSet

      // for each row of a table, we need a <TR> tag -- "Table Row":
      oCGI.cOutString += [<tr>]

Output of Action Types with Anchor/Link:
To output the 'actions' that a user may take, the code will appear more complex than it really is. That's because among other things we are ensuring that we are able to pass the customer number as a parameter to the program we will be calling (when the user clicks on the action), as well as actually displaying the text for the action. If you take a good look at the code below it should make sense, but we will break it down a little:

      oCGI.cOutString += [<td>]
      oCGI.cOutString += [<a href="EditCustomer.dbw?CustomerNum=]+;
                         fCustomer["CustomerNum"].value+[">]+;
                         'edit';
                         [</a>]
      oCGI.cOutString += [</td>]

      // some blank space:
      oCGI.cOutString += "&nbsp;&nbsp;&nbsp;"
      
      // delete ...
      oCGI.cOutString += [<td>]
      oCGI.cOutString += [<a href="DeleteCustomer.dbw?CustomerNum=]+;
                         fCustomer["CustomerNum"].value+[">]+;
                         'delete';
                         [</a>]
      oCGI.cOutString += [</td>]

The first and last lines are the Table Data tags for the display of the information. The second command (the one that wraps over four lines) is the important one to examine here. We are outputting an anchor tag, but note that rather than calling a web page, we are calling the program "EditCustomer.dbw". Also note that there is a question mark (?) in there, followed by "CustomerNum=". The question mark tells the CGI handler of the web server to pass what follows as parameters, and parameters in CGI applications are name/value pairs (somename="somevalue"). The next line places the customer number in the tag. After that we end the first part of the anchor tag (">) and then we tell dBASE to add to this string the type of the action (in this case, the word 'edit') - this is in the place of the text to display for the anchor. Finally we have the end anchor tag (</a>).

As we are going to be allowing the user to delete a customer's record as well, note that we have the option to delete (shown in the code above). This calls a different program, but also passes along the customer number to it.

The actual data:
The following is the rest of the output code for the data ... It's not really necessary to display the customer number, but for now we'll leave it in. It's not exactly hurting anything.

      // Customer Number:
      oCGI.cOutString +=  [<td>] 
      oCGI.cOutString +=  fCustomer["CustomerNum"].value
      oCGI.cOutString +=  [</td>] 

      // Customer Name:
      cName = fCustomer["FirstName"].value.rightTrim()+" "+;
              fCustomer["LastName"].value.rightTrim()
      oCGI.cOutString += [<td>]
      oCGI.cOutString += cName
      oCGI.cOutString += [</td>] )

      // City:
      oCGI.cOutString += [<td>]
      oCGI.cOutString += fCustomer["City"].value
      oCGI.cOutString += [</td>]

      // Postal Code:
      oCGI.cOutString += [<td>]
      oCGI.cOutString += fCustomer["PostalCode"].value
      oCGI.cOutString += [</td>]

      // we are done with this row, add the end TR tag:
      oCGI.cOutString += [</tr>]

      // move to next row in table (bad idea to leave this out!):
      rCustomer.next()

   enddo

   // close off the table:
   oCGI.cOutString += [</table></center>]

   // output the output string:
   oCGI.streamDetail( oCGI.cOutString )

The end of the program ...:
The rest of the code for the program is pretty straight-forward, and is just used to add some final output and end the program:

   // done with the data ...
   // add an anchor tag to go back to the main application menu
   oCGI.cOutString += [<p><hr><p>]
   oCGI.cOutString += [<a href="../index.htm">Main Application Menu</a>]

   // ---------------------------------------------------
   // stream out the footer:
   oCGI.streamFooter()

   // we're done. While not absolutely necessary, this can't
   // hurt:
   quit

// code not shown for the end of the try/catch ...

Back to the Menu


The Editing Program
Note that in order to edit a row, you have to know what row to edit. If you have looked at the code to display the customer data (above), you will recall that the word 'edit' has an HTML anchor tag that points it at the edit program, and passes as a parameter the customer number.

We have to, in our code, get this information and use it to find the row in the table that we need to edit and display. Once we have found the row, then we need to let our user see it. If it turns out that for some reason that row is not in the table (either a problem in our code or someone has deleted it, or ...?), then we need to tell the user that.

// we should have a parameter and we need to try to find
// the value passed:
try
   if not rCustomer.applyLocate( ["CustomerNum="+oCGI["CustomerNum"] )
      oCGI.sorryPage( "Customer Number: "+oCGI["CustomerNum"]+" was not found ..." )
      quit
   else
      rCustomer.filter = "CustomerNum="+val( oCGI["CustomerNum"] )
   endif
catch( Exception E )
   oCGI.errorPage( e )
endtry

Note that "sorryPage()" is a method of the main web class that we are using.

Assuming we get past this point, we need to send the HTML through to the user to display the current row, with the ability to edit the data.

What I have done with this code is to place all of the code necessary to output the customer data as an editable screen into a method of the web class. The reason for this is that it will be used in at least two places, and updating the screen design more than once is difficult and can be confusing after awhile. (What if you add something in one screen, but not the other? What if the design is different in one screen than the other?).

There are some things that get done before displaying the screen, however.

   try
      // First some really basic stuff (center, some title stuff, 
      // end/center):
      oCGI.cOutString += [<center><font size="+2">Customer Data</font><br>]
      oCGI.cOutString += [Date: ]+date()+[</center>] )
      oCGI.cOutString += [<p>] )

      // Need to stream out the 'begin form' tag, with the name of the
      // CGI application we'll call when the user clicks the "Submit"
      // button:
      oCGI.getFormBegin( "EditCustomer2.dbw" )

The code shown above displays some header information, including the date, and then it calls the method to return the tag that defines the beginning of a form. This tag, when used with a CGI application (as opposed to the sample code given in this paper when discussing HTML form controls), needs to have an attribute called "method" which defines the type of action to execute, and an attribute called "action" which defines the program or HTML page to be loaded when the user submits this page, and finally it is a good idea to use the "enctype" attribute. What needs to be actually streamed out is a tag that looks something like:

   <form method="post" action="EditCustomer2.dbw" enctype="application/x-www-form-urlencoded">

Display/edit the data
The method getFormBegin() simply takes the name of the program used in the action attribute, and builds this string, then returns it.

Next we need to call the screen used to edit the data:

   // display the editing screen:
   oCGI.getCustomerScreen("Edit")

This command calls the method getCustomerScreen(), and tells it that we are going to be editing a row. By using a parameter we can call that code from here and the append program, and it can create the appropriate controls. With the 'edit' mode, we are going to be pulling data from the row that was selected.

The following is a small example of some of the code in the getCustomerScreen() method, just to give an idea how it works. If you need to really examine it, open the class in the source editor ...

       // If adding a new row, we can set any defaults we might
       // want to set, and that should be it:
       parameter cType

       // first, a table:
       this.cOutString +=  [<table border="1" width="100%">]

       // For this to work in a sensible fashion, we're going
       // to output the data with each field being in its own
       // row of the HTML table. It just makes it easier ...
       // hopefully this will make sense as you see it.

       // if we're editing a row, let's display the customer
       // number:
       if cType == "Edit"
          this.cOutString +=  [<tr valign="top">]
          this.cOutString +=  [<td align="right">Customer Number: </td>]
          this.cOutString +=  [<td>]+fCustomer["CustomerNum"].value+[</td>]
          this.cOutString +=  [</tr>]

          // we also need to create a hidden value that can
          // be passed invisibly to the next program:
          this.cOutString += [<input type="hidden" name="CustomerNum" value="]+;
                             fCustomer["CustomerNum"].value+;
                             [">]
       endif

       // Next the name fields:
       // First Name:
       this.cOutString +=  [<tr valign="top">]
       this.cOutString +=  [<td align="right">First Name: </td>]
       this.cOutString +=  [<td>]
       // create the entryfield, but we need to check to see
       // what's up -- if we're editing we need to pass the
       // current value ...:
       this.cOutString +=  [<input type="text" name="FirstName" size="20" maxlength="20"]+;
          iif( cType == "Edit", [ value="]+fCustomer["FirstName"].value.rightTrim()+["], "" )+;
          [>]
       this.cOutString +=  [</td>]
       this.cOutString +=  [</tr>]
       // Last Name:
       this.cOutString +=  [<tr valign="top">]
       this.cOutString +=  [<td align="right">First Name: </td>]
       this.cOutString +=  [<td>]
       // create the entryfield, but we need to check to see
       // what's up -- if we're editing we need to pass the
       // current value ...:
       this.cOutString +=  [<input type="text" name="LastName" size="20" maxlength="20"]+;
          iif( cType == "Edit", [ value="]+fCustomer["LastName"].value.rightTrim()+["], "" )+;
          [>]
       this.cOutString +=  [</td>]
       this.cOutString +=  [</tr>]

As noted, that is just an example. There's quite a bit more. Notice that we check to see if we are in "Edit" mode, and we are, we modify our processing to return the currently selected value for the fields.

Hidden?
An important thing to note is the hidden value for the customer number. We are displaying the customer number in the form, but as a "read-only" (i.e., text) display. In order to pass this along to the next program, we need to output it in some fashion. We could have opted to display the customer number in a 'text' control, but that would imply to the user that they can change this number -- and of course if they did change the number problems would occur when attempting to save the record. HTML provides us with an alternate way of doing this for our CGI applications, and that is the "hidden" control. It acts like other controls in that it needs a name, and a value, but as it does not actually display, the user is not ever going to see it. This value gets passed along to the next program exactly like the items that are displayed.

Back to the Edit Program
Once we have the screen displayed, we need to finish up the bottom of the screen:

   // here we set up the pushbuttons at the bottom of the screen:
   oCGI.cOutString += [<center>] )
   oCGI.cOutString += [<input type="submit" value="Save Changes">] )
   oCGI.cOutString += [&nbsp;&nbsp;&nbsp;] )
   oCGI.cOutString += [<input type="reset" value="Abandon Changes">] )
   oCGI.cOutString += [</center>] )

   // end of the form:
   oCGI.cOutString += [</form>] )

   // add an anchor tag to go back to the main application menu
   oCGI.cOutString += [<p><hr><p>] )
   oCGI.cOutString += [<a href="../index.htm">Main Application Menu</a>] )

   // stream the screen out:
   oCGI.streamDetail( oCGI.cOutString )

   // ---------------------------------------------------
   // stream out the footer:
   oCGI.streamFooter()

// the rest of the code is the 'quit' command and the end of the try/catch/finally
// structure ...

Now That We Have The Data, What Next?
Well, the <form> tag specifies another program get executed. Why do we need to do that? A well behaved Windows application keeps the row active and we can just issue a call to the rowset.save() method. Well, you have to realize that while your application is running on a Windows server, once the data is sent to the browser, your program has stopped execution -- in other words, there is nothing for it to do at this point. The program titled "EditCustomer" has completed its work, and is done. Web applications are quite different from Windows applications, and you have to keep that in mind as you design them ...

In this case, we are going to call a second program that will find the customer number, and then replace the data in the row with the data that the user has entered and passed when the "Submit" button was pressed.

What happens when the user submits a form is that the values of the various web controls get passed through the server to your dBL code. You then need to know what to do with that information.

The program will be called (for lack of a better name) EditCustomer2.dbw. The beginning of the program is the same as for EditCustomer.dbw. However, from this point it does change a bit. Once we have found the row, we need to actually write the data to the fields in the table. For most controls this is pretty straightforward, but we are using radiobuttons and checkboxes, and we have to handle those a little differently, for example.

Another thing to keep in mind is that the values are being returned as elements of the oCGI object, and they are returned as elements of the associative array. Associative Arrays are case sensitive, which is something that throws a lot of dBASE developers off, as most of the time dBASE is not case sensitive when it comes to referencing objects, variables, and such.

Data Validation
The first thing we should do is deal with any data validation -- make sure that the user entered data correctly based on any criteria required by your application and the table design. The only thing that I am going to do for data validation for this very simple application is to check to make sure that there are actually values in the first and last name fields.

The following is an excerpt from the data validation code:

   // ----------- Validation of Data ---------------
   // This is the spot to do that -- before we try
   // actually updating anything ...
   // For this example, the only real validation
   // I'm going to do is make sure the first and
   // last names are filled in. You can get as complex
   // as you need to.
   if empty( oCGI["FirstName"] )
      oCGI.sorryPage( "Customer First Name is required! It cannot be left empty.",;
                      "Data Error" )
      quit
   endif

That doesn't look that bad, and it's not. What this does is pretty basic -- it checks the name/value pair that was passed to the CGI object in dBASE (which is an associative array) to see if it's empty. Since this particular object is an HTML 'Text' object (equivalent to a dBASE entryfield), it will always return a value, even if it is empty. If the value is empty, we call the CGI object's sorryPage() method, and give some explanation of the problem. This method includes text that tells the user that they can click the "Back" button of their browser, and so on. The "quit" command ensures that no further processing occurs here.

Your data validation can be simple, as above, or it can be much more complicated, based on the needs of your specific application. If, for example, you needed to verify a credit card number, you could put all that here ...

Saving the Data
Once your program gets past any data validation, then you of course need to save the data to the table. This can get a bit more complex, and we're just going to look at the basics here. The reason for the complexity is that in the case of checkboxes and textarea controls, a value may be returned, or not. So you have to check for these.

Here's some code:

   // now to actually attempt to save the data ...:
   rCustomer.beginEdit()

   // the way we do this is assign values for fields
   // from the CGI array, in most cases. When we get to
   // the radiobuttons and checkboxes it's a little
   // different:
   fCustomer["FirstName"].value    := oCGI["FirstName"]
   fCustomer["LastName"].value     := oCGI["LastName"]
   fCustomer["Address1"].value     := oCGI["Address1"]
   // etc. This is trimmed from the code in the actual program ...

That's pretty simple stuff. The next bit gets a bit more complicated, but it's not horrible:

   // now we deal with the logical fields, which were done
   // using checkboxes. CGI handles checkboxes differently
   // than entryfields -- if the value is returned, the
   // checkbox was checked, if not, it was unchecked. There
   // is no null value here -- it's either considered to be
   // true or false:
   fCustomer["ContactViaEMail"].value  := oCGI.isKey( "ContactViaEMail" )
   fCustomer["ContactViaPhone"].value  := oCGI.isKey( "ContactViaPhone" )
   fCustomer["ContactViaPostal"].value := oCGI.isKey( "ContactViaPostal" )

What the code is doing is if there is a value returned by the checkbox, then we tell dBASE that the value is true, if "isKey()" returns a false value, then the checkbox was unchecked in the HTML page, so we store a false value in the table. Note that HTML does not deal with 'null' values for checkboxes. If you really need null values for your logical fields, then you may want to use either radiobuttons or a combobox (select object in HTML/CGI apps).

The rest of the code for saving the data is pretty straightforward:

   // Radiobuttons -- in this case, the "MaleFemale" ('sex') buttons:
   // In this case CGI returns the 'value' of the selected
   // radiobutton:
   fCustomer["MaleFemale"].value := oCGI["MaleFemale"]

   // Next we have values from a listbox:
   // Lisboxes return the 'selected' value:
   fCustomer["AgeGroup"].value   := oCGI["AgeGroup"]

   // and finally, the notes/memo field -- taken from
   // a textarea -- note, if it's empty it is not
   // passed:
   if oCGI.isKey( "Notes" )
      fCustomer["Notes"].value       := oCGI["Notes"]
   endif

   // save everything:
   rCustomer.save()

Finishing up, and The Response Page
At this point, the data has been saved to the table. We could stop here, and not do anything else, but your web app, to be a proper CGI application, should always return a response page. The response page in this case can be as simple as telling the user that the data was saved, and then allowing them whatever options you wish ...

   // The Response Page:
   // First some really basic stuff (center, some title stuff, 
   // end/center):
   oCGI.cOutString += [<center><font size="+2">Data Saved</font><br>]
   oCGI.cOutString += [Date: ]+date()+[</center>]
   oCGI.cOutString += [<p>]

   // give the user some options as to what to do ...
   // (html links to DisplayCustomers and index.htm ...)
   oCGI.cOutString += [<p><hr><p>]
   oCGI.cOutString += [<center>]
   oCGI.cOutString += [<a href="DisplayCustomers.dbw">View/Edit Another?</a>]
   oCGI.cOutString += [&nbsp;&nbsp;&nbsp;]
   oCGI.cOutString += [<a href="../index.htm">Back to Main Menu</a>]
   oCGI.cOutString += [</center>]

   // stream the whole thing out:
   oCGI.streamDetail( oCGI.cOutString )

   // the standard footer:
   oCGI.streamFooter()

   // quit:
   quit

// The rest of the code in this program is the end of the try/catch, and the cleanup
// code ...

Back to the Menu


Deleting A Row
The only real coding part that needs to be done is dealing with deleting a row. The beginning of the code will be similar to the code in the Edit program, but we will not be actually editing anything. We will display SOME of the data from the row, ask the user if they really want to delete it, and so on. This is similar to the way most Windows applications handle deleting a row in a table.

As with the edit option, we will have two programs, but they are both relatively simple.

In the first, we will display some of the data from the row that the customer has asked be deleted, and they must then click on another link to actually delete it. We could have used a JavaScript confirm dialog, but that gets more complex than I wanted to get into here, so we're going for simple.

The following is what some of the code looks like:

   // First some really basic stuff (center, some title stuff, 
   // end/center):
   oCGI.cOutString += [<center><font size="+2">Delete Customer Data</font><br>]
   oCGI.cOutString += [Date: ]+date()+[</center>]
   oCGI.cOutString += [<p>]

   // Display some of the customer data -- this is "lifted"
   // from GS_WebClass.cc, the GetCustomerScreen method,
   // and modified to display-only, and not all data is
   // displayed.

   // first, a table:
   oCGI.cOutString +=  [<center><table border="1">]

   // Customer Number:
   oCGI.cOutString +=  [<tr valign="top">]
   oCGI.cOutString +=  [<td align="right">Customer Number: </td>]
   oCGI.cOutString +=  [<td>]+fCustomer["CustomerNum"].value+[</td>]
   oCGI.cOutString +=  [</tr>]

   // Next the name fields:
   // First Name:
   oCGI.cOutString +=  [<tr valign="top">]
   oCGI.cOutString +=  [<td align="right">First Name: </td>]
   oCGI.cOutString +=  [<td>]
   oCGI.cOutString +=  fCustomer["FirstName"].value.rightTrim()
   oCGI.cOutString +=  [</td>]
   oCGI.cOutString +=  [</tr>]

   // code trimmed -- there is more in the actual program

   // finally let's end the table:
   oCGI.cOutString +=  [</table>]
   oCGI.cOutString +=  [<p>]

   // Give the user the option to delete this by clicking
   // on a link. We could use a JavaScript confirm() dialog,
   // but that takes more work, and we're keeping this
   // simple:
   oCGI.cOutString += [<a href="DeleteCustomer2.dbw?CustomerNum=]+;
                      fCustomer["CustomerNum"].value+[">]+;
                      [Click Here to Delete This Customer Record]+;
                      [</a>]

   // some blank space:
   oCGI.cOutString += "&nbsp;&nbsp;&nbsp;"

   // Give user the option to return to Display ...:
   oCGI.cOutString +=  [<a href="DisplayCustomers.dbw">]+;
                      [Return to Display of Customers]+;
                      [</a>]

   oCGI.cOutString += [</center>]

Delete A Row, Part 2
Okay. That was the first part of the process. Once the user clicks on the link to actually delete the record, what happens? It's pretty simple. The program DeleteCustomer2 is very similar to this one, it shows the same data, but rather than displaying the link to delete the row, we do the following:

   // Actually Delete the record:
   rCustomer.delete()

   // let the user know ...
   oCGI.cOutString += [<font color="red">Customer record deleted from customer table.</font>]
   oCGI.cOutString += [<p>]

Back to the Menu


Adding New Data
This code is remarkably similar to the code used to edit data, so less explanation will be added. All I intend to do is show the basic changes necessary in the code to add a row, etc.

This is really simple. We can copy the edit code in EditCustomer, and then make the changes necessary -- I have boldfaced the parts that were changed:

   // First some really basic stuff (center, some title stuff, 
   // end/center):
   oCGI.cOutString += [<center><font size="+2">New Customer Data</font><br>]
   oCGI.cOutString += [Date: ]+date()+[</center>]
   oCGI.cOutString += [<p>]

   // Need to stream out the 'begin form' tag, with the name of the
   // CGI application we'll call when the user clicks the "Submit"
   // button:
   oCGI.getFormBegin( "AddCustomer2.dbw" )

   // display the editing screen:
   oCGI.getCustomerScreen("Add" )

   // here we set up the pushbuttons at the bottom of the screen:
   oCGI.cOutString += [<center>]
   oCGI.cOutString += [<input type="submit" value="Save">]
   oCGI.cOutString += [&nbsp;&nbsp;&nbsp;]
   oCGI.cOutString += [<input type="reset">]
   oCGI.cOutString += [<center>]

   // end of the form:
   oCGI.cOutString += [</form>]

Adding A Row, Part 2
For part one, that is really all we need to do. For part two, we will have to do very similar things as in the second part of the customer edit, including data validation. As a matter of fact, if handled properly, we can copy that program and again make minor changes.

   // now to actually attempt to save the data ...:
   rCustomer.beginAppend()

   // the way we do this is assign values for fields
   // from the CGI array, in most cases. When we get to
   // the radiobuttons and checkboxes it's a little
   // different:
   fCustomer["FirstName"].value    := oCGI["FirstName"]
   fCustomer["LastName"].value     := oCGI["LastName"]
   
   // from here down, the rest of the code is very similar,
   // except for the response page ... so we're skipping down 
   // to there:

   // The Response Page:
   // First some really basic stuff (center, some title stuff, 
   // end/center):
   oCGI.cOutString += [<center><font size="+2">New Customer Saved</font><br>]
   oCGI.cOutString += [Date: ]+date()+[</center>]
   oCGI.cOutString += [<p><hr><p>]
   oCGI.cOutString += [<center>]

   // show new customer number:
   oCGI.cOutString += [Customer added: <font color="blue">]+;
                      fCustomer["CustomerNum"].value+;
                      [</font>]

   // and from here it's very much like before ...

Back to the Menu


An HTML Menu/Front-End
We're going to give the users of this application a simple HTML menu.

This is simple, and is in the code samples as "index.htm". The file has a header, two menu options, and a footer. That's all that is really needed for this very simple web application.

Back to the Menu


Pulling It All Together
Rather than using the project explorer in dBASE to create our executables, I have found it easier to just create a simple program that builds the executables. This can be run and when done all of the executables are done and ready to go.

For this application the program looks like:

   // This program is used to create the executables used for
   // the sample application for dBCon 2004 by Ken Mayer:
   // it compiles each file that needs compiling, then
   // it builds the actual executables, using the .DBW file
   // extension. Details on this can be found in the OLH,
   // and in the paper for the conference.

   // This is the path to where the exes will be stored:
   #DEFINE EXEPath "..\Executables\"

   // check for image files, copy if needed:
   if not file( EXEPath+"PoweredBydBASE.gif" )
      copy file PoweredBydBASE.gif to EXEPath+"PoweredBydBASE.gif"
   endif
   if not file( EXEPath+"StagHead.bmp" )
      copy file StagHead.bmp to EXEPath+"StagHead.bmp"
   endif

   // Compile individual programs and required source code:
   compile :WebWizards:webclass.cc
   compile GS_Webclass.cc
   compile DisplayCustomers.prg
   compile EditCustomer.prg
   compile EditCustomer2.prg
   compile DeleteCustomer.prg
   compile DeleteCustomer2.prg
   compile AddCustomer.prg
   compile AddCustomer2.prg

   set safety off
   // Build the exes:
   build DisplayCustomers.pro, :webwizards:webclass.co, GS_Webclass.co to ;
         EXEPath+"DisplayCustomers.dbw" WEB
   build EditCustomer.pro, :webwizards:webclass.co, GS_Webclass.co to;
         EXEPath+"EditCustomer.dbw" WEB
   build EditCustomer2.pro, :webwizards:webclass.co, GS_Webclass.co to;
         EXEPath+"EditCustomer2.dbw" WEB
   build DeleteCustomer.pro, :webwizards:webclass.co, GS_Webclass.co to;
         EXEPath+"DeleteCustomer.dbw" WEB
   build DeleteCustomer2.pro, :webwizards:webclass.co, GS_Webclass.co to;
         EXEPath+"DeleteCustomer2.dbw" WEB
   build AddCustomer.pro, :webwizards:webclass.co, GS_Webclass.co to;
         EXEPath+"AddCustomer.dbw" WEB
   build AddCustomer2.pro, :webwizards:webclass.co, GS_Webclass.co to;
         EXEPath+"AddCustomer2.dbw" WEB

   // Just so it looks like something happened:
   ? "Application Built and ready to go ..."
   set safety on

Running this program will compile the source code and build the executables from the source code, storing it in the location designated by "EXEPath". Using the "web" parameter means that the runtime will not attempt to load any of the normal dBASE User Interface controls, making the load time much faster for each executable.

Back to the Menu


Wrap-Up

As noted repeatedly through this paper, this is a simple application. One might ask "Why did you do things in this fashion rather than ...?" and the basic answer is that "You could do it that way, this is the way it made sense to me at the time ...". Just as with regular Windows applications, dBASE Web programming is quite flexible, and there are many ways one can approach coding just about anything. Is there "one right way?" Not really. Find a method that works for you. There may be a more efficient way, but as long as what you are doing works for you and your users, it is "correct".


IIS Issues:
There are various issues involved with using IIS (the default web server that comes with Windows NT, XP, etc.) and dBASE Web Applications. A few issues that came up when various folk were asked are described here.

"... an issue from a programming point of view is that the application's current folder is the server's root. The following is, I believe, Bowen's (Moursund) suggestion for handling this. I've added to my subclass of webclass.cc and then forget about it." -- Michael Nuwer

   // Ensure that working directory is app directory:
   set directory to left(_dbwinhome, len(_dbwinhome)-1 ) 

One glitch that Bowen encountered was on the server for the dBASE Bug System: "I think that the IIS/Windows security on the machine was misconfigured, and that the problem is not a general problem. The solution found by trial and error was to bypass the security glitch by ensuring that all web applets had 8.3 names."

Also from Bowen Moursund:
"Some IIS and general tips off the top of my head:

- The account used for anonymous access may need to be given more permissions that the stock anon account, maybe 'power user' permissions, and of course must be able to access any databases used by web apps.

- The DBASE_SUPPRESS_STARTUP_DIALOGS=1 environment setting is critically important.

- Use Apache instead <g>."

Of course, you may not have any choice as to whether or not Apache is used, so ...


BDE Issues:
The Borland Database Engine (BDE) can only handle 48 users concurrently. In many cases with a dBASE Web application this is not really much of an issue, as the code executes and is done relatively quickly. Below is some code provided by Bowen Moursund (via Michael Nuwer again) for this -- basically it allows you to tell the user to try again if the limit is hit:

   #define BDE_CLIENTSLIMIT 35

   if not UnderBDEClientsLimit( BDE_CLIENTSLIMIT )
      // return a 'Server Busy' page
      QUIT
   endif

   function UnderBDEClientsLimit( nLimit )
      local bIsUnder
      bIsUnder = true
      try
         s = replicate(chr(0), 7)
         extern CINT DbiGetSysInfo(CPTR) Idapi32
         DbiGetSysInfo( s )
         bIsUnder := ( s.getByte(6) < nLimit )
      catch (exception e)
      endtry
   return bIsUnder


Other sources to learn more about dBASE Web Applications

NOTE: I spent a bit of time updating this so that the links all go somewhere, and anything that didn't was removed ...

Michael Nuwer's pages:
A Web Tutorial

dBASE Developer's Bulletin Articles:
A Web Application
Query/Response Wizard
HTML Table Response Page for the Web
BDE Version Test for Web Servers
Building a Web Application with the Web Classes
One-Click Web Application in Six Easy Steps
Creating Reports for the Web: WebReports.cc

Sample Applications:
Ken's dBASE Website -- this includes a web-based message board program ...

John Creed's Applications ("The first one is a project management tool for scaffolding inventory & rent, other inventory, payroll and invoicing. The second one is an online store that uses my shopping cart system. The last one has an onLine application form, and a private virtual onLine office center to run their business.")
www.realtimecosting.com
www.bestpriceblinds.com

Christopher Neumann's application ("All images are generated with a dbw applet (Also, this is using reverseproxy with Apache :D where the web server in the intranet streams the image thru the primary web server to the webuser. (many thanx to Mike & John for helping set this up)):
http://www.landisc.com

dBASE Knowledgebase:
Includes a different article with a different approach written some time ago by myself, in the Intermediate section. There is an article in there as well on comparing the dBASE form controls to Web form controls.

Back to the Menu


This paper is copyright ©2004 by Ken Mayer. It is for use of the dBASE community to learn something about developing web applications. The code that goes with this paper is also copyright ©2004 by Ken Mayer, and may be used in part or in whole by dBASE developers to learn and create dBASE web applications, with the caveat that credit should go where credit is due -- either my code, or code that I borrowed, which will be documented as such ...