{INCLUDE _WEB_HDR.HTM}

Part 7: Putting Program Flow Together

Now you can control how a web page is sent to the user and the user can supply information back to the web server, so how do you structure a web application in an environment that has no real concept of a session. This is the true challenge of creating a web application. Instead of working top-down, web applications work inside-out and are based on "events" instead of program flow.

The easiest way to start, is by looking at a simple, but useful example:

You company wants to allow it's customers to query the status of outstanding orders. Each customer has a customer number and has been assigned a password so that they cannot see each other's information.
This is a simple three-page web application.
Page 1 asks the customer for an account number and a password.

Page 2 displays all of the outstanding orders as a list.

Page 3 displays details for a particular order.

Here are the three pages layouts:
Welcome to On-Line Order Status

Please enter your customer information:

Customer Account #: Password: 


Select the order(s) you wish to see details for
Order Number Order Date Purchase Order Amount
031075 Jan 17, 1997 P74994 1475.73
031076 Jan 18, 1997 P74995 300.71

Here is the information that you have requested
Order 31075:

    Order entered Jan 17, 1997 by J. Higgens
    Ship to:  American Products
              271 Main street
              Fairbanks, MN  88871
    Contact:  George Jeffrey x 181

Qty  Part #  Style
 3   887453  White/Brown shirt
 5   887454  White/Grey shirt 

Order 31075:

    Order entered Jan 17, 1997 by J. Higgens
    Ship to:  American Products
              271 Main street
              Fairbanks, MN  88871
    Contact:  George Jeffrey x 181

Qty  Part #  Style
 8   887453  White/Brown shirt
 4   887454  White/Grey shirt 
In addition, you will want to create several error pages:

A Sample Application

This is what these pages look like to the end-user. When you create these pages with an HTML editor, you leave out all of the data specific information and just include the overall layout.

Page 1

Page one contains two text input fields and a "submit" button. The form calls page 2 in it's action with a method of POST. Page 1 does not contain any embedded MV/Basic code.

Page 2

Page two receives the results of page 1. The page itself is painted with the HTML editor to look like:
Select the order(s) you wish to see details for

{RAW(TABLE)}


It contains the following MV/Basic code:
~PicLan-IP/BASIC { }
*
      OPEN 'ACCTS' TO ACCT.FILE ELSE STOP 201,'ACCTS'
      OPEN 'ORDERS' TO ORDER.FILE ELSE STOP 201,'ORDERS'
*
      PL_GETVAR ACCT.NO FROM 'ACCT' ELSE ACCT.NO = ''
      PL_GETVAR PSWD FROM 'PSWD' ELSE PSWD = ''
*
      READ ACCT FROM ACCT.FILE , ACCT.NO ELSE
         CALL PLW.PAGE('BADACCT.HTM','',R.ERR,RESX)
         RETURN
      END
*
      IF PSWD <> ACCT<10> THEN
         CALL PLW.PAGE('BADPSWD.HTM','',R.ERR,RESX)
         RETURN
      END
*
      EXECUTE 'SSELECT ORDERS WITH STATUS = "OPEN" AND WITH ACCT = "' : ACCT.NO : '"'
*
      EOF = 0
      NO.ORDERS = 0
*
      LOOP
         READNEXT ID ELSE EOF = 1
      UNTIL EOF DO
         READ ORDER FROM ORDER.FILE , ID THEN
            IF NOT(NO.ORDERS) THEN
               TABLE = '<table border=1 width=100%>'
               LINE = '<tr>'
               LINE = LINE : '<th>Order Number</th>'
               LINE = LINE : '<th>Order Date</th>'
               LINE = LINE : '<th>Purchase Order</th>'
               LINE = LINE : '<th>Amount</th>'
               LINE = LINE : '</tr>'
               TABLE<-1> = LINE
            END
            NO.ORDERS = NO.ORDERS + 1
            LINE = '<tr>'
            LINE = LINE : '<td><input type=checkbox name="A':CNT:'" value="':ID:'">':ID:'</td>'
            LINE = LINE : '<td>' : OCONV(ORDER<1>,'D') : '</td>'
            LINE = LINE : '<td>' : PL_QUOTE(ORDER<2>)  : '</td>'
            LINE = LINE : '<td align=right><tt>' : OCONV(ORDER<3>,'MD2') : '</tt><td>'
            LINE = LINE : '</tr>'
            TABLE<-1> = LINE
            CNT = CNT + 1
         END
      REPEAT
*
      IF NOT(CNT) THEN
         CALL PLW.PAGE('NOORDERS.HTM','',R.ERR,RESX)
         RETURN
      END
*
      TABLE<-1> = '</table>'
      TABLE = PL_AMTOCRLF(TABLE)
At first, this may appear to be a daunting bit of logic, but when broken down, it is really quite simple.

The first part of the MV/Basic code opens files. In general, you will need to open files with every subroutine call unless you save file variables in named common. The PicLan-IP web server uses standard common, so named common is usually easier to manipulate. If you must use standard common, the PicLan-Ip web server allocates an array of 500 unused variables at the front of the common block for your application code to use.

After the files are open, the code retrieves the values for ACCT.NO and PSWD. These values are then used to retrieve an item from the ACCTS file. If the account number is invalid, the logic chains to the page BADACCT.HTM. If the password does not match, the logic chains to the page BADDPSWD.HTM. If the account number and password both match, the logic continues.

In this example, an ACCESS SSELECT statement is used to retrieve the orders that meet the specified criteria. In most cases, you would replace this SSELECT with some sort of cross-reference or index lookup function that the application maintains. When the select list is retrieved, one of two cases can occur. If the list is empty, the logic chains to the page NOORDERS.HTM. If there are orders present, the MV/Basic code builds an HTML table "on the fly" that will be inserted as a single element into page 2. This is an example of where the MV/Basic code must actually know something about HTML layouts and tags. Because the table is variable in size, you cannot simply use a fixed number of simple insertion points, but must instead build a variable length table based on the size of the select list. Fortunately with HTML, building a table is a bit verbose, but simple never the less. If you do not know how to build an HTML table, build a sample HTML document with your HTML editor and then copy the table's layout into MV/Basic.

This example table has a header row and well as one detail row for each order on file. The detail row contains a checkbox form input field along with some additional display fields. The input field allow the user to check off the order(s) that they wish to see detail for.

Some notes on HTML quoting are important here. The insertion point for table is coded as {{RAW(TABLE)}}. The RAW(...) function indicates that the PicLan-IP web server should merge the variables without quoting HTML tag characters first. This is necessary because the TABLE variable that is being inserted intentionally includes HTML tags.

When building the TABLE variable, the function PL_QUOTE is used to convert the string contained in ORDER<2> to a string that is compatible with HTML. You see, certain characters such as less than '<' are not allowed within HTML documents unless they are first converted to a multi-character string that is interpreted the same way. The < characters is stored in HTML as &lt;. You could actually use the PL_QUOTE(...) function for all of the fields, but numbers and dates are known not to contain HTML quoted characters, so using PL_QUOTE(...) is unecessary.

After the table is built, the variable NO.ORDERS contains the size of the table. This variable is merged with a hidden input field so that the next page can easily scan for which orders are selected.

And finally, the PL_AMTOCRLF(...) function is used to convert the MultiValue attribute marks into HTML CRLF sequences.

Page 3

The last page actually displays order detail for the selected orders. The page itself look like:
Here is the information that you have requested
{RAW(RESULT)}
It contains the following MV/Basic code:
~PicLan-IP/BASIC { }
*
      OPEN 'ORDERS' TO ORDER.FILE ELSE STOP 201,'ORDERS'
*
      PL_GETVAR NO.ORDERS FROM 'NO.ORDERS' ELSE NO.ORDERS = 0
*
      LIST = ''
      FOR I = 1 TO NO.ORDERS
         PL_GETVAR ID FROM 'A' : I THEN
            LIST<-1> = ID
         END
      NEXT I
*
      IF LIST = '' THEN
         CALL PLW.PAGE('NOSELECT.HTM','',R.ERR,RESX)
         RETURN
      END
*
      I1 = DCOUNT(LIST,AM)
      FOR I = 1 TO I1
         READ ORDER FROM ORDER.FILE , LIST<I> THEN
            R = 'Order ' : LIST<I> : ':'
            R<-1> = ''
            R<-1> = 'Order entered ' : OCONV(ORDER<2>,'D') : ' by ' : ORDER<14>
                ... more code to build order detail information
            RESULT<-1> = R
            IF I < I1 THEN
               RESULT<-1> = '<hr>'
            END
         END
      NEXT I
*
      RESULT = PL_AMTOCRLF(RESULT)
Again, the logic here is pretty straightforward. First the NO.ORDERS variable is retrieved from the posted page. This is then used to retrieve all of the checkbox variables and build a list of item ids to display. Once you have the list, it is a simple task to build a normal ASCII display for each order using whatever data you wish. <hr> tags are added between each entry to insert an HTML horizonal rule and the final result is merged in CRLF format into the final page.

Summary

At first, this example looks complicated, but in fact most of the MV/Basic code involved deals with building formatted displays and interpretting data. The actually handling of web processing is done in only a few lines.

{INCLUDE _WEB_FTR.HTM}