Tech News Back Issues Issue: 020603

Introduction to the Omnis Web Client: Part 9, Log on to Web client using HTML interface

By Dr Caroline Wilkins
iB2B Systems Ltd

http://www.artificia.co.uk
caroline@ib2bsystems.com

In the previous newsletters, we created an Omnis web client application that enabled a user to enter records, upload images, view and change data and images. This is a fairly basic application, but one that illustrates some of the technicques involved in web client application development.

Web client applications can support many users but, in practice, we would often like to restrict user access to the application. The HTML interface may be very usefully employed as a log on interface for a web client application. This technique prevents large numbers of public users from accessing a web client application that may be intended and licensed for a select group of users. In this newsletter, we will go through a technique which uses the Omnis HTML interface, otherwise known as the ultra-thin client, to log on to a web client application.

If you want to skip the stages in the previous newsletters, you can download the zip file RPUpdate.zip
containing RescuePet.lbs and RescuePet.df1 (libraries & datafiles may need to be converted in Omnis). The numbering in this issue follows on directly from the last issue. Make sure that you either put your library and datafile in c:\RescuePet\ directory or change the path to the datafile in the Startup_Task $construct method:

Set hostname {C:\RescuePet\RescuePet.df1}

39. HTML: Hello World

Firstly, a quick introduction to the elements of HTML that you will need in order to get started with the HTML interface to Omnis. The basic structure of an HTML page is:

<HTML>

<HEAD>
</HEAD>

<BODY>
</BODY>

</HTML>

The <HTML> and </HTML> tags indicate the beginning and end of an HTML document. Within the HTML document there is a HEAD and BODY section. The HEAD section takes elements such as TITLE and META tags. It is good form to give your page a title. (The META tags can wait til you are ready to publish and promote your site. You won't need them yet.) The BODY section of the HTML document is where you put the content of the page. Try this for a simple "Hello World" application:

<HTML>

<HEAD>
<TITLE>Hello World page</TITLE>
</HEAD>

<BODY>
Hello World
</BODY>

</HTML>

Just put the code into a text file using a text editor, save as hello.html and open it with your browser.

40: HTML: Tables

There is a lot that can be said on the formatting and design of HTML pages. That is really outside the scope of this newsletter though. We will just cover the material necesssary to put together a demo page. It is useful to know how to format an HTML table however. This will enable you to set out your data entry pages tidily.

A table begins with a <TABLE> tag and ends with a </TABLE> tag. In between there are rows. Each row begins with <TR> i.e. Table Row and ends with </TR>. Within each row, there are elements which begin and end with <TD> and </TD> respectively.

So, if we add this to the HTML we have already, with some text for a log on and a little paragraph centre formatting:

<HTML>

<HEAD>
<TITLE>Hello World page</TITLE>
</HEAD>

<BODY>
<P align=center>

<TABLE>
    <TR>
        <TD>Username</TD>
        <TD>Form box to go here</TD>
    </TR>
    <TR>
        <TD>Password</TD>
        <TD>Form box to go here</TD>
    </TR>

</TABLE>
</BODY>

</HTML>

Again, there is plenty that can be done to format a table attractively, but this is beyond the scope of this newsletter. There are many resources on the web that will cover this subject in detail.

41: HTML: Forms

In order to send data from an HTML page to an application running on an Omnis server, we use an HTML form. A general purpose HTML form has the structure:

<FORM method="get" action="http://www.mydomain.com/scripts/test.cgi">

<INPUT TYPE="text" NAME="Data1" VALUE="Defaultvalue1">
<INPUT TYPE="text" NAME="Data2" VALUE="Defaultvalue2">

<INPUT TYPE="submit" NAME="Submit" VALUE="Submit">

</FORM>

The form can have method="get" or method="post". If you choose "get" the parameters will appear appended to the target URL and the user will see something like this in the address bar of their browser:

http://www.mydomain.com/scripts/test.cgi?Data1= Defaultvalue1&Data2= Defaultvalue2
If you choose method="post" the parameters will not be visible.

41: HTML: Logon Form

If we combine the above, we get a log on page using:

<HTML>

<HEAD>
<TITLE>Log on </TITLE>
</HEAD>

<BODY>
<P align=center>
<FORM method="get" action="http://www.mydomain.com/scripts/test.cgi">

<TABLE>
    <TR>
        <TD>Username</TD>
        <TD><INPUT TYPE="text" NAME="Username" value="Username"></TD>
    </TR>
    <TR>
        <TD>Password</TD>
        <TD><INPUT TYPE="text" NAME="Password" value="Password"></TD>
    </TR>
</TABLE>
<INPUT TYPE="submit" NAME="Submit" VALUE="Submit">
</FORM>

</BODY>

</HTML>

42: HTML: Logon to Omnis

This page would work just fine if there was a cgi script at http://www.mydomain.com/scripts/test.cgi to receive the data and log on. We have to tailor the page to target an Omnis web client application and this involves a few extra hidden parameters. We also change the action of the FORM tag to target Omnisapi.dll or nph-omniscgi.exe (either may be used; the first is for Microsoft IIS, the latter is for Apache servers or similar). This is a resource that behaves like a script by receiving http get or post requests and forwarding them to the Omnis server, using the internal port specified by the OmnisServer parameter.

<HTML>

<HEAD>
<TITLE>Log on </TITLE>
</HEAD>

<BODY>
<P align=center>
<FORM method="get" action="http://localhost/scripts/Omnisapi.dll">
<INPUT TYPE="hidden" NAME="OmnisClass" VALUE="rtLogon">
<INPUT TYPE="hidden" NAME="OmnisLibrary" VALUE="RescuePet">
<INPUT TYPE="hidden" NAME="OmnisServer" VALUE="5912">

<TABLE>
    <TR>
        <TD>Username</TD>
        <TD><INPUT TYPE="text" NAME="Username" value="Username"></TD>
    </TR>
    <TR>
        <TD>Password</TD>
        <TD><INPUT TYPE="text" NAME="Password" value="Password"></TD>
    </TR>
</TABLE>
<INPUT TYPE="submit" NAME="Submit" VALUE="Submit">
</FORM>

</BODY>

</HTML>

43: HTML: Test Logon

In the example above, we are sending the data from the form to a remote task called rtLogon within a library called RescueDog with Omnis serverport set to 5912. To test whether this works:
43.1 Open up the demo library we have been creating in previous newsletters (or download it from RPUpdate.zip).
43.2 Create a new remote task and name it rtLogon
43.3 In rtLogon, create a parameter variable called pParams, of type Row.
43.4 Create two local vairables of type character: lUsername and lPassword
43.5 Add the following code in the $construct method of rtLogon:

Calculate lUsername as pParams.Username
Calculate lPassword as pParams.Password

43.6 Place a breakpoint on one of those lines and close the method editor
43.7 Go to Tools - Options on the main Omnis menu. Set serverport to 5912. This corresponds to the OmnisServer parameter in the HTML form.
43.8 Locate either Omnisapi.dll or nph-omniscgi.exe in the webclient\server\webserver directory of your Studio installation. Copy the file to the scripts directory of the webserver running on your local machine. For windows users this would be C:\Inetpub\wwwroot\scripts. It doesn't matter what platform you are testing this on, as long as you are using a valid webserver path, the resource permissions are set to permit execution and the directory path where you have put the script corresponds to the URL you are using in the action element of the HTML <FORM> tag.

43.9 Open up the HTML page we have created, enter data values for Username and Password and click the submit button. If all has gone to plan, you should hit the breakpoint you set in rtLogon. Stepping through, you should see the values you entered for Username and Password calculated from pParams into lUsername and lPassword.

If this has not worked thus far, check the following:

<FORM method="get" action="http://localhost/scripts/Omnisapi.dll">

1) Check that action points to a valid resource on your webserver.
2) Check that the resource or directory has been configured to enable execution within your webserver.

<INPUT TYPE="hidden" NAME="OmnisClass" VALUE="rtLogon">

3) Check that you have named the remote task in your library rtLogon

<INPUT TYPE="hidden" NAME="OmnisLibrary" VALUE="RescuePet">

4) Check that the library you are testing is called RescuePet. (It should be, if you are using the demo library we have been building.)

<INPUT TYPE="hidden" NAME="OmnisServer" VALUE="5912">

5) Check that the serverport property within Omnis is set to 5912. (Tools - Options)
In practise, it doesn't matter what port you decide to use, as long as it doesn't clash with a port in use by another application on the same machine. Any high numbered port should be OK. 5912 is just a popular choice for Omnis web applications because it is the example given in the Studio documentation. Do make sure that, whatever port you decide to use, it is the same within Omnis and within the hidden parameter of the HTML page that is calling Omnis.

If all has gone to plan, you should have a library that looks something like RPLogon1.zip (libraries & datafiles may need to be converted in Omnis) and an HTML page which passes data to a remote task within it. Next time, we will continue with this logon technique and show how it can be used to validate users and pass back an html page containing a web client application.

 


 

Exploring Tokens

By David Swain
Polymath Business Systems
www.polymath-bus-sys.com
dataguru@polymath-bus-sys.com

In the last issue of Omnis Tech News (and many other times in the past) I have mentioned that certain items within an Omnis Studio applications are "tokenized". I have recently seen or heard other people mention that one thing or another is "tokenized" in support of their arguments that their techniques are more efficient than others, but an examination of the facts shows that many of these people are just using the term for marketing purposes and don't really know what they're saying.

So how can I so confidently talk about what is "tokenized" and what is not? Twelve years ago I discovered how to examine the tokens in an expression string in Omnis. I even wrote an article about it eleven years ago in the first quarter of 1992 in my old OmniScience newsletter (Volume II Number 1). Some new twists have been added since then (not to mention a number of new Omnis Studio developers), so it's about time to revisit the topic.

What Is A Token?

A token is a pointer to a resource within an application. It is usually a short string, often only a few bytes long, that acts as an "alias" for some item within the application (which would usually have a much longer name). Another way to look at it is as a "pseudo machine code" version of the item in question.

In actual fact, the token is simply the way that Omnis Studio internally regards that item. All the verbose names for things we see as we develop applications are just for our benefit as human programmers. We are presented with the proper string label for a variable name, a function name, a method command or any of a variety of items Omnis Studio tokenizes wherever we use them in an application. But what we see is just for show!

This is also why we can share code with colleagues who speak a different language and the bulk of our code (except for explicit strings and names that we supply) will appear in their localized copy of Omnis Studio in their language. Omnis Studio presents this code based on the local resources of that copy of itself.

Tokenized Interpretation

Omnis Studio is an "interpreted" programming language. That means that whenever some routine within the application is invoked, the program (Omnis Studio) must read the source code one line at a time, interpret what that line means and then carry out that instruction. Since Omnis Studio is also tokenized, this process is streamlined. The job of converting commands and other items to machine code has already been done. If we use care in our programming (so that we use truly tokenized items when there is a choice to be made), the interpretation step is all but eliminated!

But just how can we know which items are tokenized and how much storage and interpretation this is likely to save? For that, we need a little help...

Evaluation Functions

There are two functions in Omnis Studio that perform an amazing feat: They convert a string value into an expression and then they evaluate that expression to yield a result. These are the eval() and evalf() functions. They are offered to us to provide both extra flexibility and, with an in-depth knowledge of their use, increased programming power. I am thoroughly impressed by this set of functions and have found them to be extremely useful in exploring the inner workings of Omnis Studio (as well as other things). I hope to make you as enthusiastic as I am about their potential!

So what do these functions have to do with exploring tokens? When we understand exactly what they do, this becomes very clear. One of them is the very instrument we need to "see" the tokens used by Omnis Studio for many commonly used elements within our applications.

Strings as Calculations

The basic purpose of both of these functions is to convert a string value into an expression and to evaluate that expression. (For a further discussion of how Omnis Studio treats strings and expressions, refer to the article "Literals vs Expressions" in Omnis Tech News published in March of 2001 - one of the very first issues.) The differences between these two functions are as follows:

The basic evaluation function is eval(). It takes a single parameter, which can be either an explicit string or a string-yielding expression (a variable name alone or something more complex). If the parameter is other than an explicit string, the expression is first evaluated to a string value. The string value of the parameter is then interpreted as an expression in its own right and, assuming that the expression is valid, the result of that expression is returned. The data type of the returned value is that which would normally be expected, e.g., if the original string were "dat(#D+5)", the eval() function would return a date value.

The evalf() function is even more powerful, but is also more restrictive. It only accepts a variable name as a parameter. That variable must be of a string type (Character or National) and contain a string that can be converted into a valid expression to not generate an error. Here's why: When the evalf() function has converted the string value held in that variable, it replaces the original string value with the tokenized version of the converted expression. This is done to enhance execution speed if the same string variable value needs to be evaluated again (in a Repeat loop, perhaps). An indicator (ASCII character "1") is also included at the beginning of the replaced string so that Omnis Studio will know whether the variable contains tokens or needs to be reconverted.

This is the key to examining the tokens in an expression. All we have to do is examine what Omnis Studio places into a variable processed by evalf() on a character-by-character basis.

Testing for Validity

Since an invalid expression will halt processing with a devastating error, it is good practice to trap for invalid expressions in a method before using eval() or evalf(). The Test for valid calculation method command is provided for this purpose. If an expression is not valid, this command simply sets the Flag to "false" instead of generating an error. We can then abort the process before our users are faced with their windows disappearing or the Debugger window coming to the top. Here is how we would use this command.

Test for valid calculation {evalf(variable)}
If flag false
  ;perhaps an OK message here
  ;or a breakpoint if debugging
  Quit method
End if

The command can also be used with the eval() function. It can, in fact, be used with any expression, but it is most useful for checking these functions as shown above.

Now let's examine how we can use this technology to explore how Omnis Studio tokenizes items.

Tracking Tokens

Since the evalf() function replaces the original expression stored in a string variable with the actual tokens that represent the variable names, operators and functions in that expression, I took the liberty of doing a little "reverse engineering" to further explore how Omnis Studio works. Specifically, I wanted to see how it tokenizes the components of an expression to be able to make better decisions in the future about streamlining the expressions in my applications.

To do this, I created a little method that calculates #S1 as an expression string, calculates #S2 as evalf(#S1), displays #S1 (now tokenized), the length of #S1 (so we can see how many characters are in the tokenized string) and #S2 in an OK message, and then displays the ASCII value of each character in #S1 (the tokens placed there by evalf()) in subsequent OK messages since many of these tokens are not printable ASCII characters. Here is that method:

Calculate #S1 as '<expression to examine>'
OK message {[#S1]}
Test for valid calculation {evalf(#S1)}
If flag false
  OK message {Bad expression}
  Quit method
End If
Calculate #S2 as evalf(#S1)
OK message {#S1 = [#S1]//len(#S1) = [len(#S1)]//#S2 = [#S2]}
For count from 1 to len(#S1) step 1
  OK message {asc(#S1,[count])=[asc(#S1,count)]}
End For

By changing the expression string calculated for #S1 and then executing the method with Command-E, I was quickly able to compile the tables below and determine many other things about expressions in Omnis Studio. Here are some of the results:

First, every expression restored by evalf() begins with an ASCII character "1". This indicates that the string contains tokens and only needs to be evaluated if processed again by evalf() - no additional conversion required!

Functions are treated like special types of open parentheses - a closing parenthesis (ASCII "6") is used to denote the end of the parameter list for each one. I was also gratified to note that a unary minus (for negation) has a separate token (ASCII "25") from the minus used for subtraction (ASCII "2"), since I have been telling students for 17 years that it is a separate operator and takes precedence over the multiplication and division operators in the various generations of Omnis.

Numerals in numeric (or string) values take on their standard ASCII values as does a period used as a decimal point. Characters used to represent explicit string values (those that follow the ASCII "39", the single quote character) also are stored and interpreted by their standard ASCII values. The hyphen (ASCII "45") and the slash (ASCII "47") only seem to appear in this context.

Most variables are represented by two bytes (characters). The first indicates the File Class (or other group) that the variable belongs to and the second indicates the variable number within the File Class. The first File Class created in or copied to the application is number 129, the next is number 130, etc. The library must, therefore, maintain a table of these for easy access. When we consider that there is a "low byte" (0- 127) and a "high byte" (128-255) part of a byte, number 129 is equivalent to number 1 on the high byte side. In past generations of Omnis, we were only allowed up to 99 File Classes within an application, so the range of File Class numbers is 129 to 227. (I have not tested beyond this limit yet...)

File Class number 228 indicates the system variables (also known as "hash" variables). The variable numbers of these are shown in the second table below. I have included those hash variables that are now listed as "Obsolete" since they are still defined in Omnis Studio and the numbers assigned to them may provide some insight into how other items are identified. Notice that there are still some large gaps in the variable numbers in this list.

Instance variables of the current class or instance are considered by Omnis Studio to be in File number 254. (This slot was used by "Format" variables in Omnis 7, if you would like a little more insight.) Local variables of the current method (including Parameters) are in File number 255. The variable number within those pseudo-Files is the order in which each variable was added to the group - not the order in which they appear in the Catalog window. Class variables are considered to be in File number 249.

Task variables, on the other hand, are prefixed by what appears to be a notation string (partially tokenized) followed by the non-tokenized variable name. Some parts of notation strings appear to be tokenized, but not the "name" portions. So "$cinst.$objs.objectname" is tokenized up to "objectname" and then it contains the literal name. Notation string segments, like "$ref", are also tokenized. But placing an unnecessary "$ref" on the end of the notation string for an item only lengthens that notation string, it does not somehow magically force that notation string to do a more efficient job of pointing to its target object.

What We Can Examine

We can't examine everything that Omnis Studio tokenizes, just those items that can be included in Calculation strings. But this still covers quite a bit of territory! We can examine variable names, operators, functions and even certain parts of notation strings.

We can learn to minimize Omnis Studio evaluation time for an expression by checking the number of tokens needed to express different forms of it. In understanding tokens and the evaluation hierarchy of operators, we can quickly learn to minimize our use of excess parentheses and other common errors.

The evaluation functions have many other uses. If interest is shown, I can devote another article to some of these possibilities. In the meantime, enjoy exploring

Operator and Function Tokens

Operator or Function Token or ASCII   Operator or Function Token or ASCII   Operator or Function Token or ASCII   Operator or Function Token or ASCII
+ 1   int( 23   - (literal) 45   atan2( 67
- (subtractn) 2   mid( 24   . (decimal) 46   mod( 68
* 3   - (negation) 25   / (literal) 47   pick( 69
/ 4   upp( 26   0 48   min( 70
( 5   dtd( 27   1 49   max( 71
) 6   dtm( 28   2 50   low( 72
> 7   dty( 29   3 51   dtcy( 73
< 8   pwr( 30   4 52   fact( 74
<> 9   sqr( 31   5 53   nam( 75
= 10   cmp( 32   6 54   cap( 76
>= 11   fld( 33   7 55   ann( 77
<= 12   dim( 34   8 56   anna( 78
& 13   tim( 35   9 57   totc( 79
| 14   dtw( 36   exp( 58   lookup( 80
not( 15   rnd( 37   ln( 59   server( 81
abs( 16   jst( 38   log( 60   eval( 82
len( 17   ' (quote) 39   sin( 61   evalf( 83
pos( 18   lst( 40   cos( 62

 

File Class variables referenced by File number (129+) and variable number within File Class
, (delimiter) 19   tot( 41   tan( 63
dat( 20   sys( 42   asin( 64
con( 21   chr( 43   acos( 65
chk( 22   asc( 44   atan( 66

 

Hash Variable (File Class "228") Tokens

1-60
1…
  STAB*
76
  T
100
  CT
118
numerics
60
  OK*
77
  F
101
  TOP
119
P
61
  CANCEL*
78
  L
102
  LSEL
120
R
62
  CLOSE*
79
  LM
103
  FDT
121
D
63
  WCLICK*
80
  LN
104
  EFLD
122
S1
64
  EDATA
81
  EM*
105
  SUBFLD
123
S2
65
  TOTOP*
82
  ER*
106
  ERRCODE
124
S3
66
  TOTOP1*
83
  EF*
107
  ERRTEXT
125
S4
67
  TOTOP2*
84
  EN*
108
  L1
231
S5
68
  SENT*
85
  FD
109
  L2
232
BEFORE*
69
  ENTER
86
  FT
110
  L3
233
BEFORE1*
70
  RETURN
87
  MU
111
  L4
234
BEFORE2*
71
  SHIFT
88
  UL
112
  L5
235
AFTER*
72
  COMMAND
89
  PI
113
  L6
236
CLICK*
73
  CTRL
90
  FDP
114
  L7
237
DCLICK*
74
  OPTION
91
  RAD
115
  L8
238
TAB*
75
  ALT
92
  RATE
116
  ???
240
 
  MODIFIED
93
  CLIST
117
   

 

 
© 2002-2003 Copyright of the text and images herein remains with the respective author. No part of this newsletter may be reproduced, transmitted, stored in a retrieval system or translated into any language in any form by any means without the written permission of the author or Omnis Software.
Omnis® and Omnis Studio® are registered trademarks, and Omnis 7™ is a trademark of Omnis Software Ltd. Other products mentioned are trademarks or registered trademarks of their corporations. All rights reserved.