4D Mixed-Mode Web Serving Techniques


ACI - Documentation Français English German ACI Technical Notes ACI Technical Notes, By Subject Back Previous Next Find

4D Mixed-Mode Web Serving Techniques:

Crossing the bridge between Contextual and Non-contextual Modes

By Eric Saltzen, 4D, Inc. Technical Support

Technical Note 00-42

Technical Notes for Technical Notes for 00-09 September 2000

Introduction


4th Dimension 6.5 includes a powerful new method of web serving known as Non-contextual Mode. In this mode, 4D becomes a "classical" HTTP server, allowing HTML pages to be easily constructed using any graphical or text based web page editor and then served directly. However, in Non-contextual Mode an interactive connection —or "session" in web parlance— is NOT maintained by 4D. If the site requires a sequence of interactions with the user that can be controlled and tracked; for example, managing a shopping cart or submitting a series of forms, then this persistent interaction must be managed manually in Non-contextual mode. There are three standard ways to track the state of a web connection:

cookies (see Tech Note #00-17 "Using Cookies with 4th Dimension Web Server" from April 2000)

hidden fields on HTML forms (see the 4D sample databases WebExam, Auction, Snap-e, and eBiz)

URL encoding (placing the current context ID information into every URL and link)

The astute reader will note that the third option above is actually the same one employed by 4D's built in Contextual Mode. For more information, see "Web Connection Context Management: the Contextual Mode" available online at <http://www.4D.com/acidoc/cmu/cmu02010.htm>. So if 4D has a built in method of session management, why not take advantage of it where appropriate? Of course, in 4D v6.0 there was no choice but to use Contextual Mode, even if it was not required by some or all of a 4D web site. In 4D v6.5, sites can still use Contextual Mode exclusively, or use it in combination with Non-contextual mode, or even not use it at all. This Tech Note illustrates how to combine static pages served in Non-contextual Mode with dynamic, interactive pages being handled by Contextual Mode - all that is required is a little careful planning and a few HTML techniques to incorporate the best of both modes.

The sample database "4DMixedModeWeb" implements a simple class roster and bulletin board that might be used by a teacher to keep in contact with students and foster a sense of community. Both Contextual and Non-contextual Modes are utilized where appropriate, with graceful handling of database session timeouts. Three different ways of submitting forms into 4D are illustrated; one using pure Contextual Mode, another using Contextual Mode with a pre-coded HTML form, and lastly a straight Non-contextual solution. It also contains a method that culls HREF anchors from HTML in order to create a link database for publication via Contextual Mode, and an example of accepting a file upload from a browser with 4th Dimension Web Server. As a bonus, I have included two methods that originated in 4D University training classes that illustrate pulling pictures out of 4D for use on the web and parsing incoming URL data into tokens (for more information about 4D Training visit <http://www.4D.com/training> . It's an investment in your future!)

A Note Regarding the New Context Referencing Mode


In the Web Server I tab of the 4D Database Properties dialog, there is a check box for enabling "New Context Referencing Mode" which slightly modifies the way in which Contextual Mode operates (see figure below). In the original Contextual Mode, the Context ID and Subcontext ID are appended to the URL of the HTML document as well as being placed into every link (HREF) on every page, this ensures that any link followed from any page will contain the information that will be analyzed by 4th Dimension to:

assign each HTTP request to the proper web process for handling (Context ID)

make sure the user has not followed a link out of order (Subcontext ID)

The New Context Referencing Mode takes advantage of the HTML tag <BASE HREF> to specify the Context and Subcontext ID's for each page. This tag specifies the base URL to use for all relative URLs contained within a document, and must appear within the scope of a <HEAD> element. When a user clicks on a relative link within a web page, their browser (also known as the "user-agent") automatically prepends the BASE HREF onto the URL. Thus by placing the Context IDs into the BASE HREF, 4D guarantees that every link available on that page will include the information when a request is sent by the user-agent. The BASE tag is included in the HTML 2 / 3.2 / 4 specifications, and is supported by Netscape 1 / 2 / 3 / 4, Internet Explorer 2 / 3 / 4 / 5, Opera 3 and WebTV - so clearly it is a very compatible feature to employ. The advantage of placing the Context and Subcontext IDs into the BASE HREF is that it only need appear once within the HTML document, not repeatedly, thus making pages smaller and faster to serve.

4DMixedModeWeb


4DMixedModeWeb is a sample database (available in both Mac and PC format) that shows how to incorporate Contextual Mode techniques into a site that has a Non-contextual home page. Any number of static pages can be added to the site simply by linking to them from the home page (and staying in Non-contextual Mode). Any number of dynamic pages can be added anywhere to the site by adding 4DMETHOD links that initiate Contextual Mode sessions. It is also possible to generate semi-dynamic pages utilizing Non-contextual mode techniques via 4DACTION and/or 4DVAR tags (see the 4D sample databases WebExam, Auction, Snap-e and eBiz to explore this avenue further).

If you examine the Database Properties, you will find that WebClassRoster is set to Use New Context Referencing Mode, Start without Context, and has a default HTML Root and Home Page set (see figure below). The database itself contains only thirteen methods: On Startup, On Web Connection, COMPILER_WEB, ParseText4Links, WEB_Bulletin_Board, WEB_Enroll_In_Class, WEB_GetPicture, WEB_List_Students, WEB_Search_Links, WEB_Search_Students, WEB_Send_Link_Search_Form, WEB_Set_Limits and WEB_Text2Array. The HTML pages for this site were originally hand-coded HTML then re-designed using Macromedia's Dreamweaver, with appropriate 4DMETHOD links to initiate Contextual Mode activities. Screenshots of the HTML code were captured from Dreamweaver's source pane.

Let's take a look at the our default home page first — this is the page that 4D will automatically send in response to an initial request from a user-agent. The On Web Connection Database Method will be covered later in this Tech Note along with why it is necessary to handle Contextual Mode sessions properly.

index.html

index.html source

The main thing to notice about the home page is its simplicity — nothing more than a few basic links. The links are categorized into Context, Context+HTML and Non-contextual according to the mode in which they operate. Let's explore the links and their 4D methods and/or HTML code from top to bottom.

List Students, Enroll in Class, Search Students, Bulletin Board

This are all contextual mode commands that invoke their 4D code via "/4DMETHOD" tags. When one of these links is requested by the user-agent, 4D initiates a new Contextual Mode Web Process and assigns it a Context ID which will be thereafter associated with all links generated from within that session. As the following methods illustrate, letting 4D do the work for you in Contextual Mode really is quite easy.

WEB_List_Students

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Database Method: WEB_List_Students C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled ALL RECORDS([Students]) DISPLAY SELECTION([Students];*) SEND HTTP REDIRECT("/")

WEB_Enroll_In_Class

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Database Method: Web_Enroll_In_Class C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled ADD RECORD([Students]) If (([Students]Name="George Washington") & (OK=1)) QUERY([Students];[Students]Name="George Washington")  ` the default value set on the form DELETE SELECTION([Students])  ` make sure we don't accumulate dummy records in data file DOCUMENT TO BLOB("deadsorry.html";$myBLOB) SEND HTML BLOB($myBLOB;"text/html";True) End if SEND HTTP REDIRECT("/")

WEB_Search_Students

  

` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Project Method: WEB_Search_Students C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled tMessage:="Use the '@' character for wildcard matching." QUERY BY EXAMPLE([Students]) tMessage:="" Case of : (Records in selection([Students])>0) DISPLAY SELECTION([Students];*) : (OK=1) DIALOG([Students];"NoRecords") End case SEND HTTP REDIRECT("/")

WEB_Bulletin_Board

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Project Method: Bulletin_Board C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled ALL RECORDS([Bulletin Board]) DISPLAY SELECTION([Bulletin Board];*) SEND HTTP REDIRECT("/")

This is standard 4D fare, and in Contextual Mode we can almost forget that we are serving to a web browser instead of 4D Client. The only time-consuming part for these commands was the cycle of creating the forms, testing them in a web browser, and then modifying them to get a satisfactory appearance. We get some data validation (e.g. required values must be filled) for free in Contextual Mode, although there is little we can do to alter the default appearance of 4D Alerts when presented in Contextual Mode (DIALOGs can of course be customized). Additionally, we can easily attach object methods to buttons without worrying about how they will be submitted to the web server — 4D handles that in Contextual Mode. Let's take a look at each form and note a few of the techniques used in conjunction with the above methods.

[Students];"WebOutput"

This form is about a simple as can be; note the image placed at coordinates {0,0} and Moved to the Back layer. It will appear as the background of the web page when this form is served in Contextual Mode from WEB_List_Students. After the user clicks the Done button, the DISPLAY SELECTION command will end and the next line of code executes. A call to SEND HTTP REDIRECT ends the Contextual Mode session immediately and instructs the user-agent to make another request for the root of the web site. At this point the web browser will return to the home page in Non-Contextual Mode.

[Students];"WebInput"

This form is used for entering new student information from WEB_Enroll_In_Class, and at that time the variable tMessage is empty so nothing appears in the web browser. The Name and Comment fields both have default values assigned to them that will be automatically filled in. In WEB_Enroll_In_Class there is a check to make sure that the user replaced the default name "George Washington" with something else to prevent incomplete records being added to the database. If the user clicks OK without entering any data, he is bounced out of Contextual Mode by a call to SEND HTML BLOB with the third parameter set to True. The page deadsorry.html is explained below. When the WebInput form is used by the method WEB_Search_Students for search key entry, the value of tMessage is pre-filled with instructions about how to enter wildcard matching searches into the form.

deadsorry.html

This HTML contains a technique that we will use later for gracefully handling a database session timeout, the HTTP-EQUIV property of the META tag which allows us to specify information that normally would need to be included as part of the HTTP header. Specifically, the REFRESH element which instructs the user-agent to pull another page from the site after a certain delay — the home page will be loaded ten seconds after deadsorry.html is displayed in this example.

[Bulletin Board];"WebOutput"

Object Method: New Posting button on WebOutput

INPUT FORM([Bulletin Board];"WebInput")
ADD RECORD([Bulletin Board])
INPUT FORM([Bulletin Board];"WebDisplayDetail")
ALL RECORDS([Bulletin Board])

[Bulletin Board];"WebInput"

Form Method: [Bulletin Board];"WebInput"

If (Form event=On Validate)
  [Bulletin Board]Your Message:=[Bulletin Board]Your Message+" - "+tName
End if 

[Bulletin Board];"WebDisplayDetail"

The forms for the Bulletin Board section of the web site are similar to those already illustrated. There is an additional button on the output form for adding entries to the bulletin board table, and separate input forms for displaying the details of an existing record as well as entering a new record (the new record form accepts two text entries, a name and message, and combines them into one text field for storage in the database).

Search Related Links and List Related Links


List Related Links is simply a link to the static HTML page "links.html" which contains a variety of useful URLs related to 4th Dimension and/or web serving issues. I wanted to make this page of links searchable from within the 4D web site, without having to maintain a link database within 4th Dimension and keep it correlated with the contents of the html file. Implemented in the On Startup method (listed below) is the parsing of the on-disk document "links.html" into individual HREF anchors that are placed into the database as records of the [Links] table. So every time the database is opened any changes to "links.html" will be automatically reflected in the [Links] table. The method WEB_Send_Link_Search_Form initiates the link search screen in contextual mode; however, instead of using a 4D form for the keyword entry a custom HTML page is sent. Note that it is not possible to send an HTML form in Non-contextual Mode and then switch into Contextual Mode by specifying a /4DMETHOD as the FORM ACTION. The form must be initially sent from within a Contextual process using SEND HTML FILE.

WEB_Send_Link_Search_Form

  ` WebClassRoster Sample Database, May 2000
  ` by Eric Saltzen - 4D, Inc Technical Support
  ` Project Method: WEB_Send_Link_Search_Form

C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled

SEND HTML FILE("search.html")
SEND HTTP REDIRECT("/")

search.html

The JavaScript functions at the top of the page are used for animating the roll-over of the Back button at the bottom of the page. Note that the form ACTION is specified as "/4DMETHOD/WEB_Search_Links", since this page was initially served from a Contextual Mode process using SEND HTML FILE, when it is submitted back into 4D the same Contextual process will be assigned the processing of the form results and calling of WEB_Search_Links.

WEB_Search_Links

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Project Method: WEB_Search_Links C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled   ` in case of a timeout, this var. will be set in On Web Connection If (keyword#"timeoutflag") $myKey:="@"+keyword+"@" QUERY([Links];[Links]HREF=$myKey) DISPLAY SELECTION([Links];*) SEND HTML FILE("") Else   ` we lost context after sending form DOCUMENT TO BLOB("timeout.html";$myBLOB) SEND HTML BLOB($myBLOB;"text/html";True) End if

The significance of the "timeoutflag" is explained below in the "timeout.html" section; essentially it is a way to detect if the Contextual Mode process timed out between sending the search form and receiving a submission. Note that the wildcard character "@" is automatically inserted before and after the user-supplied keyword to ensure the maximum number of hits.

Note the call to SEND HTML FILE("") which is necessary to allow processing to drop back to the WEB_Send_Link_Search_Form method, where the originating SEND HTML FILE("search.html") was called. This prevents a potential stack overflow memory problem which is described in more detail online in "Command SEND HTML FILE" at <http://www.acius.com/acidoc/cmu/cmu00619.htm> and in "Tech Tips: Using the command SEND HTML FILE in contextual mode" at <http://www.acius.com/tech%5Ftips/tips00%2D224.html>. It is important to conceptually understand why this call is necessary to avoid difficult to debug crashes, and to understand the sequential nature of a set of SEND HTML FILE commands. Most of the time in 4D you do not have to worry about returning control to a calling method, it is automatic once the end of a method is reached; however, with SEND HTML FILE you must explicitly exit Contextual Mode with a call to SEND HTTP REDIRECT or SEND HTML BLOB *OR* make another call to SEND HTML FILE with a blank string parameter in order to terminate the HTML mode and return 4D execution to the next line of code following the previously called (non-blank) SEND HTML FILE command.

[Links];"Output"

Form Method: [Links];"Output"

If (Form event=On Load)
  tMsg:=String(Records in selection([Links]))+" of "
  tMsg:=tMsg+String(<>numLinks)
  tMsg:=tMsg+" Links Related to 4th Dimension and/or 4D Web Serving"
End if 

This Form Method simply sets a 4DVAR to which is displayed as the header in a clickable list of URLs.

[Links];"Input"

Form Method: [Links];"Input"

If (Form event=On Load)
  tHREF:=Substring([Links]HREF;2)  ` omit the Char(1) to display raw link data
End if 

For the Input form which is reached by clicking the Full Page Record button on the Output form, I wanted to display the underlying code of the link so it would be possible to easily copy and paste from the browser without having to switch to "View Source" mode in the browser. By omitting the Char(1) which was placed into every record by the ParseText4Links Project Method, 4D will automatically escape-code all the special HTML characters so that the resulting page displays the HTML directly (instead of a clickable link as in the Output form). To see what is happening behind the scenes, pull up a record in both the Input and Output forms, select "View Source" for each one in your web browser and compare.

Timeout Issues for Contextual processes


At this point we begin to deal with some of the timeout issues we face in Contextual Mode. The Web Server Connections Timeout period is specified in the Database Properties, under the Connections tab.

After the period specified for web server connections, any Contextual Mode process that has not received a response to its last request will be terminated. If the user-agent subsequently does make a request for that same Context ID, 4D will not be able to determine which process to assign it to and will instead initiate a new Context ID and pass the request through On Web Connection.

On Web Connection

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Database Method: On Web Connection   ` assign default web connection timeout to per-process tracking variable webTimeoutMins:=<>webTimeoutMins   ` this variable is used in the page search.html, we need to set a default here keyword:="timeoutflag"   ` set the web display limits for this connection SET WEB DISPLAY LIMITS(<>webNumRecs;<>webNumPages;977)   ` if the browser is requesting anything besides one of our allowed methods,   ` we know a timeout has occurred so we send a special message page If (Substring($1;1;13)#"/4DMETHOD/WEB") DOCUMENT TO BLOB("timeout.html";$myBLOB) SEND HTML BLOB($myBLOB;"text/html";True) End if

This is where we detect timed-out web processes by checking the requested URL. We only allow requests to pass through On Web Connection if they are requesting a URL that begins with "/4DMETHOD/WEB". Any Non-contextual requests such as 4DACTIONs and simple clicks on links in Non-context pages will result in 4D's bypassing the On Web Connection method — so we don't have to deal with them here. If we do detect a non-standard URL it means that either a Contextual Mode session timed out and a URL that would normally pass directly into the corresponding Contextual process has instead wound up in On Web Connection, or a wily user has managed to request a disallowed URL (perhaps by typing it directly into their address bar). In either case we again utilize the SEND HTML BLOB command to both send a reply and immediately terminate Contextual Mode.

timeout.html

Our META REFRESH tag technique works well here to force the URL displayed in the browser back to the home page of our site. If we simply did a SEND HTML BLOB of the home page in response to the invalid (timed-out) Contextual Mode request, then the URL for that request would remain in the location display of the browser and potentially disrupt the serving of images and other elements on the page because the browser has the wrong BASE HREF. As an extra bit of user-friendliness, we display the current setting of the Web Server Connections Timeout using the 4DVAR "webTimeoutMins" which is set in On Web Connection for each process and database-wide in On Startup.

Going back to WEB_Search_Links for a moment, note the setting of the process variable 'keyword' to the value "timeoutflag" in On Web Connection. The only way that WEB_Search _Links could be called with "timeoutflag" in the keyword variable is if the Context timed-out in between sending the search form and it being submitted back into 4D. If that happens we terminate Contextual Mode and redirect the user back to our home page.

When testing Timeout conditions, it is extremely useful to know how to cause an immediate timeout. Of course, you can always set the Web Server Connections Timeout to the minimum value of one minute and simply wait until the process times out naturally. Nonetheless, a trip to the 4D Runtime Explorer will make it possible to verify the connection has been timed out, and can be used to terminate a contextual web process thus effectively timing it out immediately. The Runtime Explorer window can be called from the Design environment by choosing the 'Runtime explorer' command in the Tools menu. You can also display this window with the CTRL+Shift+F9 shortcut, however, the user needs to belong to a group that has access to the Design environment. This window contains four pages, named Watch, Process, Break, and Catch. From the Process tab you can locate a contextual web process, e.g. "Web Connection# 1062280704", and by clicking the Abort button simulate an immediate timeout of the connection associated with that process.

For more information about the invaluable Runtime Explorer, see "Inside the Runtime Explorer" by Jean-Yves FOCK-HOON available online at <http://www.acius.com/ACIDOC/CMU/CMU79849.HTM>.

On Startup

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Database Method: On Startup   ` This method reads in the links.html file and parses it for the links it contain   ` the results are placed in a table for easy searching with QUERY later C_TIME($linksDocRef) C_TEXT($linksText) ARRAY TEXT(linksArray;0) C_INTEGER(<>numLinks) $linksDocRef:=Open document("links.html") If (OK=1) RECEIVE PACKET($linksDocRef;$linksText;32000)  ` can handle up to 32K text file CLOSE DOCUMENT($linksDocRef) <>numLinks:=ParseText4Links ($linksText;->linksArray) ALL RECORDS([Links]) If (Records in selection([Links])>Size of array(linksArray)) DELETE SELECTION([Links]) End if ARRAY TO SELECTION(linksArray;[Links]HREF) Else ALERT("Unable to open file 'links.html' for parsing. Using cached copy.") End if   ` set the timeout for the Web Connection processes <>webTimeoutMins:="5" SET WEB TIMEOUT(Num(<>webTimeoutMins)*60)   ` set the defaults for this database <>webNumRecs:=10 <>webNumPages:=10

The first part of this method is the filling of the [Links] table with the HREF anchors culled from "links.html". This begins by simply opening the file and reading in a single packet of up to 32K of text (larger files would have to be handled in 32K chunks). Then a call to ParseText4Links which processes the text and fills the text array "linksArray" with the results (see listing below). If there are at least as many links in the file as there were the last time On Startup was run, then they are simply overwritten with a call to ARRAY TO SELECTION which will add records if necessary. If there are fewer links in the file than records in the [Links] table, then the records are deleted en masse before the call to ARRAY TO SELECTION to avoid leaving any old links lying around.

Next the interprocess variable <>webTimeoutMins is set, which will be used to set a process variable in each Contextual process for display (if necessary) in "timeout.html" via 4DVAR.

Lastly we set the defaults that will be used for the SET WEB DISPLAY LIMITS section of the web site which is covered in more detail below.

ParseText4Links

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Project Method: ParseText4Links   ` $1: Pointer to Text to be search for HREFs   ` $2: Pointer to Text Array to hold results   ` $0: Number of Links Gathered   ` This method is fairly picky and will only detect links that begin w/ exactly   ` "<a href" and end with "" - malformed or out-of-order tags will result   ` in an error ALERT message C_TEXT($1) C_POINTER($2) C_LONGINT($0) ARRAY TEXT($2->;0) C_LONGINT($location;$beginAt;$endAt) C_TEXT($mySubstring) For ($location;1;Length($1)) $mySubstring:=Substring($1;$location) $beginAt:=Position("<a href";$mySubstring) If ($beginAt=0)  ` no more HREF anchors to process $location:=Length($1) Else $endAt:=Position("";$mySubstring)   ` check for no close anchor tag, or close tag before open anchor If (($endAt=0) | ($endAt<$beginAt)) $msg:="Malformed HREF anchor tag in 'links.html'."+(Char(Carriage return )*2) $msg:=$msg+"Parsed "+String($location-1)+" of "+String(Length($1))+" characters." $msg:=$msg+(Char(Carriage return )*2)+"Collected "+String(Size of array($2->)) $msg:=$msg+" link(s)." ALERT($msg) $location:=Length($1) Else $element:=Size of array($2->)+1 INSERT ELEMENT($2->;$element) $2->{$element}:=Char(1)+Substring($mySubstring;$beginAt;$endAt-$beginAt+4) $location:=$location+$endAt+2 End if End if End for $0:=Size of array($2->)

setlimits.html - Set Display Limits

The following page is served in Non-contextual Mode and contains an HTML form that is processed via a 4DACTION call to the 4D method WEB_Set_Limits with an encoding type of 'multipart/form-data' to allow the submission of a binary graphic file to be used as the "Full Page Record" buttons which are used in lieu of double-clicking an output form row in a web browser. Note the input of type "file" with the name "pictureBLOB" and the 'accept' property set to "text/html". Immediately following the HTML form is a listing of the 4D Method used to process it.

setlimits.html source

Web_Set_Limits

 

 ` WebClassRoster Sample Database, May 2000

  ` by Eric Saltzen - 4D, Inc Technical Support   ` Project Method: WEB_Set_Limits C_TEXT($1;$2;$3;$4;$5;$6)  ` so database won't break compiled If (Undefined(maxRecords))  ` in case user tries to reload /4DACTION/WEB_Set_Limits COMPILER_WEB End if If (Num(maxRecords)<=0)  ` did user enter an invalid maxRecords? maxRecords:=String(<>webNumRecs)  ` reset to default End if If (Num(maxPages)<=0)  ` did user enter an invalid maxPages? maxPages:=String(<>webNumPages)  ` reset to default End if If (BLOB size(pictureBLOB)>0)  ` did user select a file for uploading? C_STRING(255;$fileHeader) $offset:=0  ` BLOB to text requires a variable for the offset parameter $fileHeader:=BLOB to text(pictureBLOB;Text without length ;$offset;255)   ` process the beginning of the converted BLOB and look for filename $fileNameBegin:=Position("filename=";$fileHeader)+10 For ($i;$fileNameBegin;255) If ($fileHeader[[$i]]=Char(Double quote )) $fileNameEnd:=$i $i:=255 End if End for $fileName:=Substring($fileHeader;$fileNameBegin;$fileNameEnd-$fileNameBegin)   ` process the beginning of the converted BLOB and look for content-type $contentTypeBegin:=Position("Content-Type:";$fileHeader)+14 For ($i;$contentTypeBegin;255) If ($fileHeader[[$i]]=Char(Carriage return )) $contentTypeEnd:=$i $i:=255 End if End for contentType:=Substring($fileHeader;$contentTypeBegin;$contentTypeEnd-$contentTypeBegin)   ` strip off header information, hopefully leaving just a valid image DELETE FROM BLOB(pictureBLOB;0;$contentTypeEnd+3) $err:=AP Read picture BLOB (pictureBLOB;imageVar) If ($err=0) SET PICTURE TO LIBRARY(imageVar;977;$fileName) msgText:="Successfully read the picture file '"+$fileName+"'." Else msgText:="Unable to read the picture file '"+$fileName+"', error "+String($err)+"." End if Else   ` user did not submit an image, read current settings for display contentType:="not changed" msgText:="" End if   ` set the new limits <>webNumRecs:=Num(maxRecords) <>webNumPages:=Num(maxPages)   ` report results back to user SEND HTML FILE("showlimits.html")

Although unecessary in a compiled database, the check for Undefined(maxRecords) is required to prevent a potential run-time error in interpreted mode. To see the error, simply comment out the call to COMPILER_WEB and try to re-load showlimits.html in your web browser after an initial successful submission. This error may not occur every time you attempt to produce it since 4D will re-use web processes if possible and therefore an immediate attempt to reload or refresh showlimits.html may end up being handled by the same process which initially handled the submission — and therefore the process variables we are trying to protect may indeed already be present in the process. However, if you wait a few moments before reloading the page you should be able to generate the following error (again, with COMPILER_WEB commented out for testing purposes). Also, this error is browser-dependent and depends upon how the user-agent handles the re-submission of HTML forms — but since we want our 4D web site to run on as many browsers as possible we should prevent this error.

Next we check to make sure the user has entered reasonable values in for the Maximum Number of Records per Page and Maximum Number of Page Links, and reset them to their defaults if necessary. Last but not least, we process the incoming image (if the user selected one for submission in their browser) by parsing through the first 255 bytes of the incoming BLOB and searching for the 'filename' and 'content-type' attributes which will be displayed in the results page, and then passing what's left to the AP Read picture BLOB command from the ACI Pack Plug-in — see "Command AP Read picture BLOB" available online at <http://www.acius.com/acidoc/cmu/cmu61969.htm>. If a picture is successfully extracted from the submitted BLOB, we place it into 4D's picture library with a call to SET PICTURE TO LIBRARY - see "Command SET PICTURE TO LIBRARY" available online at <http://www.acius.com/acidoc/cmu/cmu00566.htm>. If we are unable to extract a valid picture file from the submitted BLOB, then we assign the current values for display in showlimits.html.

There is a file included in the database folder, "SampleRawBLOBUpload.TXT" which contains the results of uploading a small GIF file to 4D. I used the BLOB TO DOCUMENT command to create the file from an initial upload, then used the document as a template to design the 4D method that processes it - see "Command BLOB TO DOCUMENT" available online at <http://www.acius.com/acidoc/cmu/cmu00526.htm>.

showlimits.html

showlimits.html source

Again, the HTML begins with several JavaScript routines that are used to animate the Go Back button at the bottom of the page. Then we have several 4DVAR tags used to display the settings that were just accepted or to display the defaults if the values entered by the user were invalid. There is a very interesting 4DACTION tag included in this document:

The methods "Web_GetPicture" and "WEB_Text2Array" are from the ACI University web serving course (for more information about 4D Training visit <http://www.4D.com/training>) They are both extremely generic and likely to be useful in almost every 4D web serving database. "WEB_GetPicture" is designed to be called as a 4DACTION that will extract a picture from the Picture Library, the Resource fork of the database structure, a process picture variable, or a picture field in a currently loaded record. "WEB_Text2Array" is a text parsing method that simply allows a string to be chopped up into a sequence of array elements based on a delimiting sequence of characters. Calling it with the third parameter equal to "/" as in WEB_GetPicture allows us to parse a requested URL into its component parts.

WEB_GetPicture

If (False)
    `  Method: WEB_GetPicture
    ` Borrowed from the ACI University Programming Classes
    ` Please visit <http://www.4D.com/training> for info
    ` Created By: Kent D. Wilbur
    ` Date: 2/20/00
  
    ` Purpose: Returns a picture
  
    ` Possible calls to use this routine are
    ` WEB_GetPicture/870   Would return picture ID 870 from the Picture Library
    ` WEB_GetPicture/-16892   Would return picture ID -16892 from the Resource fork
    ` WEB_GetPicture/-14709*   Would return picture ID 14709 from the Resource fork
    ` WEB_GetPicture/pMyPictVar   Would return the picture stored in pMyPictVar
    ` WEB_GetPicture/2/7   Would return the picture from table 2 field 7
  
    ` Variables for Insider
  <>f_Version6x50:=True
  <>fK_Wilbur:=True
  
End if 

  ` Declare parameters
C_TEXT($1;$tURL)  `This parameter must always be declared

  ` Declare local variables
C_BLOB($oMyBlob)
C_LONGINT($LFieldNumber)
C_LONGINT($LPosition)
C_LONGINT($LResouceNumber)
C_LONGINT($LSizeOfArray)
C_LONGINT($LTableNumber)
C_PICTURE($gThePicture)
C_POINTER($pMyObject)
C_STRING(80;$sVarName)

ARRAY TEXT(WEB_atURL;0)

  ` Reassign for readability
WEB_Text2Array ($1;->WEB_atURL;"/")

$LSizeOfArray:=Size of array(WEB_atURL)
Case of 
  : ($LSizeOfArray=2)  `We are looking to a field for the picture
    $LTableNumber:=Num(WEB_atURL{1})  ` Table number is the first parameter
    If (($LTableNumber>0) & ($LTableNumber<=Count tables))
      $LFieldNumber:=Num(WEB_atURL{2})  ` Field number is the second parameter
    End if 
    If (($LFieldNumber>0) & ($LFieldNumber<=Count fields($LTableNumber)))
      $pMyObject:=Field($LTableNumber;$LFieldNumber)
      $LType:=Type($pMyObject->)
      If ($LType=Is Picture )
        $gThePicture:=$pMyObject->
      End if 
    End if 
    
  : ($LSizeOfArray=1)  ` Make sure there is at least one parameter
    $LResouceNumber:=Num(WEB_atURL{1})
    If ($LResouceNumber#0)  ` We are looking for a resourse
      If ($LResouceNumber>0)
        GET PICTURE FROM LIBRARY($LResouceNumber;$gThePicture)
      Else 
        $LPosition:=Position("*";WEB_atURL{1})
        If ($LPosition>0)
          GET PICTURE RESOURCE(Abs($LResouceNumber);$gThePicture)
        Else 
          GET PICTURE RESOURCE($LResouceNumber;$gThePicture)
        End if 
      End if 
    Else 
      $pMyObject:=Get pointer(WEB_atURL{1})
      RESOLVE POINTER($pMyObject;$sVarName;$LTableNumber;$LFieldNumber)
      If ($sVarName=WEB_atURL{1})
        $LType:=Type($pMyObject->)
        If ($LType=Is Picture )
          $gThePicture:=$pMyObject->
        End if 
      End if 
    End if 
End case 

If (Picture size($gThePicture)>0)
  PICT TO GIF($gThePicture;$oMyBlob)
End if 

SEND HTML BLOB($oMyBlob;"Image/gif")
SET BLOB SIZE($oMyBlob;0)
ARRAY TEXT(WEB_atURL;0)
  ` End of method

Web_Text2Array

If (False)
    ` Method: WEB_Text2Array (Text;->Array;String (10))
    ` WEB_Text2Array (Text to parse;->Array to parse into;Delimiter)
    `
    ` Borrowed from the ACI University Programming Classes
    ` Please visit <http://www.4D.com/training> for info
    ` Created By: David Adams
    ` Date: 3/21/97
    ` Revised By: Kent Wilbur
    ` Date: 4/28/97
  
    ` Purpose:
    ` This routine takes a text block and parses it into array elements.
    ` The text block can be a variable or field, but if it is a variable it must 
    ` already exist.  The array can be a string or text array, but if you use a 
    ` string array make sure that each item can fit in an array element.
    ` The array must already exist. Items are added to the end of the array:
    ` ** existing elements are not modified **.
    `
    ` 4D does not support pointers to local variables or arrays, so any variable or
    ` array used must be process or interprocess in scope.
    `
    ` 4D Insider keyword variables:
  <>fD_Adams:=True
  <>f_Version6x40:=True
  <>fGeneric:=True
  
End if 

  ` Delcare parameters
C_TEXT($1;$tSourceText)
C_POINTER($2;$pArrayPointer)
C_STRING(10;$3;$sDelimiter)

  ` Declare local variables
C_TEXT($tCopyOfTextForItemCheck)
C_LONGINT($LPosition)
C_LONGINT($LStartingElement)
C_LONGINT($LItemsToAdd)
C_LONGINT($LLastElement)
C_LONGINT($LDelimiterLength)
C_LONGINT($LLastCharacter)

$tSourceText:=$1
$pArrayPointer:=$2
$sDelimiter:=$3

If ($tSourceText[[1]]=$sDelimiter)  ` Strip off leading delimiters
  $tSourceText:=Substring($tSourceText;2)
End if 

$tSourceTextLength:=Length($tSourceText)
$LDelimiterLength:=Length($sDelimiter)

  ` This routine adds to the end of the array, so find out the
  ` size of the array and add 1 to it to determine where to add to first.
$LStartingElement:=Size of array($pArrayPointer->)+1

  ` How many times is the delimiter in the source text?  This tells the routine
  ` how many items to add to the array.
$tCopyOfTextForItemCheck:=Replace string($tSourceText;$sDelimiter;"")
$LItemsToAdd:=Length($tSourceText)-Length($tCopyOfTextForItemCheck)

  ` Division needed when passed a multiple character delimiter
$LItemsToAdd:=$LItemsToAdd/$LDelimiterLength

  ` Add items in one block rather than one at a time for better performance.
INSERT ELEMENT($pArrayPointer->;$LStartingElement;$LItemsToAdd)

  ` What is the last element number in the newly resized array?
$LLastElement:=Size of array($pArrayPointer->)

  ` Now loop through the source text copying each line into the correct
  ` array element.
For ($Element;$LStartingElement;$LLastElement)
  
  $LPosition:=Position($sDelimiter;$tSourceText)
  $pArrayPointer->{$Element}:=Substring($tSourceText;1;$LPosition-1)
  $tSourceText:=Substring($tSourceText;$LPosition+$LDelimiterLength)
  
End for 

If ($tSourceText#"")  ` Something is left over at the end ($tSourceText did not end in the delimiter ch
  $LLastElement:=$LLastElement+1
  INSERT ELEMENT($pArrayPointer->;$LLastElement;1)
  $pArrayPointer->{$LLastElement}:=$tSourceText
End if 


  ` Strip off final ? if present.
$LLastElement:=Size of array($pArrayPointer->)
$LLastCharacter:=Length($pArrayPointer->{$LLastElement})
If ($pArrayPointer->{$LLastElement}[[$LLastCharacter]]="?")
  $pArrayPointer->{$LLastElement}:=Substring($pArrayPointer->{$LLastElement};1;$LLastCharacter-1)
End if 
  ` End of method

A Note about the "Go Back" animated roll-over button

GoBackButt.gif

GoBackButt_f2.gif

The original version of the HTML code for the animated roll-over "Go Back" button looks like this:

This is how it appears in "setlimits.html" which is served directly in Non-contextual Mode, in this case the HREF is a RELATIVE URL and 4D will prepend the server IP address onto the link before it is sent to the user-agent. For example, "index.html" will become "http://10.96.0.31/index.html" assuming that 10.96.0.31 is the IP Address of the 4D web serving CPU.

However, there is a problem with this link that will appear when served in a page that is the result of a call to a 4DACTION. In which case 4D will prepend the server IP address AND the base document URL to the relative link resulting in "http://10.96.0.31/4DACTION/index.html" which is, of course, invalid. To prevent this problem in "showlimits.html" (a page served as the result of a 4DACTION call) the HTML was modified as follows:

The HREF here is an ABSOLUTE URL, note the leading "/" which indicates the root of the web site. When 4D serves this page, it will prepend the server IP address onto the link before it is sent to the user-agent, however, the base document URL will NOT be prepended which avoids the erroneous 4DACTION in the middle of the link.

Unfortunately, I discovered a final problem with both of the above versions of the Go Back button HTML code. When the page "links.html" is parsed upon database startup for HREF anchors, the "Go Back" button link at the bottom of the page was also parsed and included in the [Links] table. Any search whose results included this link would result in the "GoBackButt.gif" appearing in the DISPLAY SELECTION with a non-functional link (not to mention the roll-over animation would no longer work). In order to avoid this, a third version of the HTML code for the animated roll-over "Go Back" button was created:

This avoids the use of an HREF altogether, instead relying on JavaScript and the user-agent's built in page history to cause the previous page to be displayed when the image is clicked ("window.history.back()"). By avoiding the "<A HREF" this link is completely ignored by the ParseText4Links method and thus will not appear in the [Links] table at all. Of course, this is only as reliable as the user-agent's history and we assume that the user just came from a link on our default home page. If the user came to the page containing this "Go Back" button by using a Bookmark or from a link on another site, then clicking the "Go Back" button may send them to the wrong page. It is preferable to use HREFs (absolute or relative) whenever possible.

Summary


This Technical Note and Sample Database provide a foundation upon which an interactive web site can be quickly and easily constructed by taking advantage of the best of Non-contextual and Contextual Modes in 4D. It should be relatively easy to use the sample database as a template for any site that needs an easy way to display static HTML as well as present interactive forms to the user without a lot of 4D or HTML programming. Performance should be good because Contextual Mode processes are reserved for sections of the site which require it, while Non-contextual Mode processes can quickly handle all other requests.

Using additional Non-contextual techniques from other example databases such as CookieMonster4D, WebExam, Auction, Snap-e and eBiz, even more dynamic content could be added to a site based on 4DMixedModeWeb. Future versions of 4D will also make it easier to add dynamic content by using an expanded HTML tag set — allowing some of the "logic" to be moved out of 4D methods into HTML pages. For an even more comprehensive survey of 4D web serving techniques, consider attending a 4D training class near you.

See Also


Using Cookies with 4th Dimension Web Server

http://www.acius.com/ACIDOC/CMU/CMU79847.HTM

Using Cookies with 4th Dimension Web Server - Macintosh Sample Database

ftp://partner@ftp.4D.com/ACI_TECHNICAL_NOTES/2000_(Partners_only)/MacOS/TN_2000_16-20_(APR)/00-17_4D_and_Cookies.hqx

Using Cookies with 4th Dimension Web Server - Windows Sample Database

ftp://partner@ftp.4D.com/ACI_TECHNICAL_NOTES/2000_(Partners_only)/Windows/TN_2000_16-20_(APR)/00-17_4D_and_Cookies.exe

Web Connection Context Management: the Contextual Mode

<http://www.4D.com/acidoc/cmu/cmu02010.htm>

4D Training - for improved mental acuity

<http://www.4D.com/training>

4D Sample Databases (see especially WebExam, Auction, Snap-e and eBiz)

http://www.4D.com/downloads/examples.html

Command SEND HTML FILE

http://www.acius.com/acidoc/cmu/cmu00619.htm

Tech Tips: Using the command SEND HTML FILE in contextual mode

http://www.acius.com/tech%5Ftips/tips00%2D224.html

"Inside the Runtime Explorer" by Jean-Yves FOCK-HOON

http://www.acius.com/ACIDOC/CMU/CMU79849.HTM

Command AP Read picture BLOB

http://www.acius.com/acidoc/cmu/cmu61969.htm

Command AP Read Picture File

http://www.acius.com/acidoc/cmu/CMU61988.HTM

Command SET PICTURE TO LIBRARY

http://www.acius.com/acidoc/cmu/cmu00566.htm

Command BLOB TO DOCUMENT

http://www.acius.com/acidoc/cmu/cmu00526.htm


ACI - Documentation Français English German ACI Technical Notes ACI Technical Notes, By Subject Back Previous Next Find