SteelBlue Language

Introduction

The SteelBlue language is an augmentation of HTML with new tags and flags suited for database, variable, and form manipulation. All HTML tags are legal in SteelBlue. In addition, SteelBlue gives you the ability to say:

<HTML>
 <BODY>
  <SQL SQL="select FIRST_NAME, LAST_NAME
            from PERSON
            where PERSON_ID = '22'"
       NAME="person">
  <H2>
   Hello
   <REPLACE VALUE="$person{FIRST_NAME} $person{LAST_NAME}">
  </H2>
 </BODY>
</HTML>
The SQL statement accesses the database and returns the first row of data in the namespace "person". The REPLACE tag takes the VALUE string, substitutes variables with their values, and prints the result into the HTML stream.

The example above demonstrates the power of SteelBlue. Not only does SteelBlue allow easy database access, it provides complex form checking, automatic error recovery, and built-in security.

Parts of a SteelBlue Program

SteelBlue was designed to encompass all aspects of form manipulation in a single script:

You can place these sections in a single file using the three containers: <HTML>...</HTML>, <CHECK>...</CHECK>, and <ACTION>...</ACTION>.

The Spam® Example

Let's say we had a Web application in which we wanted to temporarily store the user's name and e-mail address before allowing the user to continue. However, we only want to store that information if they like Spam®. This is a complete SteelBlue program fulfilling those requirements:
 <HTML>
  <BODY>
   <SHOWERRORS>
   <CENTER>
    <H2>Personal Information</H2>
    <SBFORM NAME="myform" SCREEN="welcome">
     Your name: <INPUT NAME="name" REQUIRED>
     <br>
     Your e-mail: <INPUT NAME="email" datatype="email"
                         REQUIRED>
     <br>
     <INPUT NAME="spam_check" VALUE="Y" TYPE="CHECKBOX">
      Check if you don't like Spam&reg;
     <hr> 
     <INPUT NAME="submit" type="submit">
     <INPUT NAME="cancel" type="button"
            VALUE="Cancel" 
            onClick="window.history.go(-1)">
    </SBFORM>
   </CENTER>
  </BODY>
 </HTML>

 <CHECK>
  <SBFORM NAME="myform">
   <!-- Make sure they like Spam -->
   <IF BOOL="$this{spam_check} eq 'Y'">
    <ERROR text="You must like Spam&reg; to continue">
   </IF>
  </SBFORM>
 </CHECK>

 <ACTION>
  <SBFORM NAME="myform">
   <!-- Copy form data to preferences -->
   <COPY SOURCE="this" DESTINATION="preferences">
   <!-- Save preferences to the Swap for later use -->
   <SAVE NAMESPACE="preferences">
  </SBFORM>
 </ACTION>
Let's explore the Spam® example in detail.

The HTML Section

The purpose of the HTML section is to generate HTML pages that the user can view, fill out, and submit. If there is a server-side typechecking error, or an error is thrown by an
ERROR tag in the CHECK or ACTION section, the HTML section will be redisplayed with any user entries automatically restored. In the Spam® example, the tag SHOWERRORS is used to specify where errors would be displayed, if they occur.

SteelBlue tags support links and form submissions to pages both inside and outside a SteelBlue application. A form made with an SBFORM tag indicates that the destination of the form submission is another SteelBlue page. Similarly, links to other SteelBlue pages are made with an SBREF tag. In the Spam® example, the SCREEN tag specifies that if the form is submitted and the CHECK and ACTION sections complete without error, then the HTML section of SteelBlue file "welcome" will be executed.

SteelBlue can automatically check the datatypes of the input elements on both the client and server side by adding a DATATYPE flag. (Please see INPUT tag for details on type checking). If you put a REQUIRED tag in an input element, that element becomes required, and the user will not be able to continue without filling it in. Server-side checking of datatypes and required elements is automatic; you can configure SteelBlue to check datatypes and required elements on the client side using JavaScript functions (included in the file "CheckUtils.js"). In the Spam® example, the "email" field must conform to the e-mail format. In addition, both "email" and "name" must be entered by the user in order to continue.

There are many more tags available in the HTML section, including tags for database access, file manipulation, and e-mail transmission. For more information, please see the SteelBlue Tag Reference.

The CHECK Section

The purpose of the CHECK section is to test the user's data for validity before proceeding. If there is an error, the rest of the CHECK section executes (in order to collect all possible errors), the ACTION section is skipped, and the HTML section is redisplayed with any user entries restored.

It is important to implement error checking on a form only if that particular form is submitted. For example, there may be multiple forms on a single page or the user may be able to leave a page without submitting a form (say by clicking on a link). In these cases, an SBFORM container can conditionally execute code if its associated form in the HTML section was submitted. In the Spam® example, the SBFORM container surrounds error checking code for the form named "myform".

The data submitted by a form is placed in an associative array with the namespace "this". In the Spam® example, an IF statement confirms that the user has not checked "Check if you don't like Spam$®." The contents of the IF container executes if the BOOL flag (when evaluated with the Eval language) is true.

Errors can be manually thrown using the ERROR tag. If an error is thrown, the rest of the CHECK section executes, the ACTION section is skipped, and the HTML section is redisplayed. In the Spam® example, an error is thrown if the user checks the CHECKBOX stating that they do not like Spam®.

There are many more tags available in the CHECK section, including tags for database access, file manipulation, and e-mail transmission. For more information, please see the SteelBlue Tag Reference.

The ACTION Section

The purpose of the ACTION section is to implement database, e-mail, and file transactions based on valid user data. The ACTION section executes only after the user's data has been checked with the CHECK section.

As in the CHECK section, the data submitted by a form is placed in the associative array "this". In the Spam® example, The action section copies the "this" namespace into a "preference" namespace with the COPY tag. It then stores the "preference" namespace into a server-side repository of data called the Swap for later use in other pages. Since they are surrounded with an SBFORM container, these tags are executed if the appropriate form was submitted in the HTML section.

If the ACTION section is successful, the HTML section of the destination page (determined by the SCREEN tag of an SBFORM, SBREF, FRAME, or REDIRECT tag) executes. If errors occur in the ACTION section, the originating HTML section is redisplayed with user entires restored.

There are many more tags available in the ACTION section, including tags for database access, file manipulation, and e-mail transmission. For more information, please see the SteelBlue Tag Reference.

Order of Section Execution

Web interaction occurs between the HTML and CHECK sections of a single page. In fact, if a form submission is successful, the CHECK and ACTION sections of the originating page and the HTML section of the destination page are executed one after another. For experienced CGI programmers, it seem odd at first to have the code for the manipulation of a form in the same file as the form generation, but we have found that it makes Web applications easier to create, understand, and maintain.

Variables

Since the fundamental use of SteelBlue tags is to manipulate data (either database data or form data), variables are an important aspect of SteelBlue. In SteelBlue tags, all variables contain strings. The Eval language uses type conversion to generate integers, real numbers, and Booleans from strings, but from SteelBlue's vantage point, everything is a string.

There are two types of variables in SteelBlue whose syntax are taken from Perl:

In addition, SteelBlue uses associative arrays to create regular arrays.

Singleton Variables

Singleton variables begin with $, and have names that begin with a letter followed by a combination of alphanumeric characters and "_". In SteelBlue they can be assigned using the ASSIGN tag and can be used in the flags for many SteelBlue tags in a process called variable substitution. For example:
<ASSIGN NAME="$a" VALUE="Mary Kathrine">
<REPLACE VALUE="Hello $a!">
will print out "Hello Mary Kathrine!"

Associative Arrays

Associative arrays are variables that can take a string index within curly brackets immediately following the variable name. For example:
<ASSIGN NAME="$person{FIRST_NAME}" VALUE="Mary">
<REPLACE VALUE="The first name is $person{FIRST_NAME}">
The index can either be a string constant, or any Eval expression that returns a string (for example $person{FIRST_NAME}, $person{"First Name"}, $person{$index}, or even $person{$index{HI} . "_NAME"}).

The variable name is called the namespace of the associative array. Tags like SQL or LOOP operate on whole namespaces:

<LOOP SQL="select username
            from AuthUsers
            order by username"
       NAME="user">
 <REPLACE value="$user{username}">
 <br>
</LOOP>
The LOOP container repeats its contents for each row of data returned from the database. The row of data is stored in the namespace "person" (determined by the NAME flag). The REPLACE tag prints the retrieved name in the HTML stream.

Whole namespaces can be stored into and retrieved from the Swap (a server-side repository of client data) using the LOAD and SAVE commands.

Arrays

Standard arrays are created by simple use of associative arrays. The consecutive integers starting at "0" are used as indices, and the string "LENGTH" is used as a key under which the number of array elements are kept. The command ACCUMULATE can be used to populate arrays, and the command LOOP can be used to iterate over arrays. (Please see the section on data sources for more examples of tags that can use arrays). For example:
<ACCUMULATE NAME="color_list"
             SPLIT=","
             VALUE="red,blue,green,pink">
<!-- $color_list{0}="red",
      $color_list{1}="blue", 
      $color_list{2}="green",
      $color_list{3}="pink", and 
      $color_list{LENGTH}="4" -->
<LOOP NAME="color" array="color_list">
 <REPLACE VALUE="$color{value}">
 <br>
<LOOP>
prints "red", "blue", "green", and "pink" separated by line breaks.

Arrays can be stored into and retrieved from the Swap using the LOAD and SAVE commands.

Special Variables

In SteelBlue, there are some special purpose variables and namespaces:

Special Variables
Variable or namespacePurpose
$USER{username}The user's username
$USER{institution}The user's institution
$USER{ticket}The user's session ID
$USER{this_page}The page being executed
$USER{last_page}The page previously executed
$installed{handler} Function to call in case of an SQL error (see Database Error Handlers)
$thisNamespace into which form data is placed (for CHECK and ACTION sections)
$lastNamespace into which form data is placed (for following HTML section)

Any global variable set in the CHECK section is available in the following ACTION and HTML sections. Any global variable set in the ACTION section is available in the following HTML section. Therefore, global variables can be used to pass messages to the next executing page (instead of using the Swap which causes database access). This means that an HTML section has two copies of the previous page's form data: the $this namespace (which could have been modified in the previous CHECK and ACTION sections) and the $last namespace (which has a clean copy of the form data).

The Swap

The Swap is a database table that allows whole namespaces to be stored on the server under the user's name. Because a user can modify cookies, hidden variables, or command-line options, storing information on the server is the best way to protect against malicious users. Namespaces are stored into the Swap with the SAVE tag. They are retrieved from the Swap with the LOAD tag. Namespaces are cleared (both in memory, and in the Swap) with the CLEAR tag.

For guest users, Swap data is deleted when the guest's ticket expires. For non-guest users, Swap data will last until the data is cleared with a CLEAR tag and can be used within and between sessions.

Tag Syntax

The tag syntax of SteelBlue is very similar to HTML. All tags are of the form:
<COMMAND FLAG1="VALUE1" FLAG2="VALUE2" ...>
A flag can occur without a value, in which case its value is assumed to be the empty string (""). Values without spaces do not need surrounding quotes. If quotes are required to surround the values, either back single quotes (') or back double quotes (") can be used.

If a tag is also a container, it terminates with an end tag of the form:

</COMMAND>
However, unlike most HTML interpreters, SteelBlue does not allow improperly nested containers:
<B>
 <FONT COLOR=RED>
</B><!--Illegal since B was opened before the FONT tag-->
 </FONT>

Special Characters

The easiest way to use most special characters in a SteelBlue flag is to simply place them in the string:
<EMAIL VALUE="$email" SUBJECT="hi" MESSAGE="
Dear Customer:

  Thank you for using SteelBlue.  We hope that
you find it as easy to use as we do, and we
greatly look forward to receiving any suggestions 
and improvements you have.
">
The EMAIL tag copies the newline characters directly into the e-mail set to the user $email. Flag contents also support the following escape sequences:

Character Escape Sequences
Escape SequenceOutput Character
\tTab
\nNew Line
\rCarriage Return
\0NULL Character
\\Backslash
\'Single Quote
\"Double Quotes

If the backslash precedes any character other than the ones above, SteelBlue ignores the backslash.

Use escape sequences to incorporate quotes in a string surrounded by the same type of quote:

 <REPLACE VALUE="She said, \"$quote\"">

Some flags are parsed by a Perl-like language called the Eval language (see Eval Interpretation below). As the Eval language also uses escape sequences, you must take care to properly escape special charaters:

<!-- In the following, the backslash needs to be
     escaped once for SteelBlue and once for Eval,
     leading to four backslashes -->
<IF BOOL="$char eq '\\\\'">
  The character is a backslash
</IF>
In these cases, it is much easier to use an EVAL container:
<EVAL>
 if ($char eq '\\'){
   print("The character is a backslash");
 }
<EVAL>

Variable Substitution

You can place variables for many SteelBlue tags directly in the flag values. In a process called variable substitution the SteelBlue interpreter replaces variables (both singleton and associative arrays) with their contents before the flag assignment. For example:
<ASSIGN NAME="$id" VALUE="22">
<SQL SQL="select FIRST_NAME
           from Person
           where PERSON_ID = '$id'" 
      NAME="person">
<REPLACE VALUE="The first name is $person{FIRST_NAME}">
In the SQL tag, "22" replaces "$id" before the select command execution, and in the REPLACE tag the results of the select command replaces "$person{FIRST_NAME}".

To avoid SQL errors caused by quotes, SQL tags escape the quotes for the $this or $last namespaces before execution. (Please see the section on data sources for more information.) For example:

<ASSIGN NAME="$this{Name}" VALUE="Robert O'Donald">
<SQL SQL="insert into Person (Name)
          values ('$this{Name}')">
Because the variable $this{NAME} is in the $this namespace, the quote in "O'Donald" is properly escaped before the SQL statement is executed.

In the SteelBlue Tag Reference, flags that appear in RED have variable substitution.

Eval Interpretation

Numerical, string, Boolean, and file manipulation is rather difficult (and unruly) using SteelBlue tags alone. Therefore, SteelBlue interprets some flags with the Eval language (a language similar to C and Perl). For example, the BOOL flag for an IF tag uses the Eval language to calculate the Boolean condition:
<IF BOOL="dtTime($this{START_DATE}) == dtTime('08/19/1946')">
 You were born the same day Bill Clinton was!
</IF>

In the SteelBlue Tag Reference, flags that appear in GREEN have Eval interpretation. The ASSIGN and REPLACE tags can have their VALUE flags passed through Eval interpretation if the EVAL flag is set. For example:

<assign name="$i" value="1">
<replace value="$i + 1 = ">
<replace value="$i + 1" EVAL>
will print the "1 + 1 = 2".

Large sections of Eval code can be placed in the EVAL tag. For example, the following prints the prime numbers between 1 and 100 using the Sieve of Eratoshenes:

<CLEAR NAMESPACE="composite">
<EVAL>
 $max_num = 100;
 for($i=2; $i<$max_num; $i=$i+1){
  if (! $composite{$i}){
   print($i.", ");
   for($j=2*$i; $j<$max_num; $j=$j+$i){
    $composite{$j} = 1;
   }
  }
 }
</EVAL>

JavaScript Compatibility

The SteelBlue interpreter assumes that everything outside of a tag that begins with "<" is a tag. Originally, we were going to copy items in SCRIPT containers verbatim, but we determined that the ability to use SteelBlue tags in creating dynamic JavaScript outweighs the potential problems. However, this makes the following code incorrect:
<script>
 var i = 0;
 if (i < 1) {  // Illegal: "<" not allowed 
               // outside of a tag!!!
  alert("i is less than 1")
 }
</script>
The easiest fix is to rewrite the code using ">":
<script>
 var i = 0;
 if (1 > i) {  // Much better
  alert("i is less than 1")
 }
</script>
Another possibility is to include the code in a comment (although then you will not be able to incorporate SteelBlue tags into the JavaScript):
<script>
 // <!--
 var i = 0;
 if (i < 1) {  // Legal since "<" is now 
               // in a comment
  alert("i is less than 1")
 }
 // -->
</script>

Data Sources

The tags ACCUMULATE, SQL, LOOP, EMAIL, and SBSELECT require a source of data in order to operate. For ACCUMULATE, a href="tags.html#LOOP">LOOP, EMAIL, and SBSELECT, the data sources can be one of the following (SQL can only use the SQL datasource): The list delimiter (set with the SPLIT flag) can be set to any string.

The SQL flag has special string substitution to avoid SQL errors caused by unescaped quotes. Variables from the $this or $last usually come directly from the user and are therefore escaped according to the rules from the database used (see escapeQuote). Variables from other namespaces are substituted directly without modification.

The tags ACCUMULATE and EMAIL operate on a single column, which is the first (leftmost) column for any data source. The tags SQL, SBSELECT, and LOOP operate on multiple columns, in which case the column names may be required to manipulate data.

For all data sources, the column names "0", "1", ... are used for the first, second, ... columns respectively. The number of columns is placed in the column name "LENGTH". This allows namespaces that are the result of LOOP and SQL tags to be used as arrays. For example, if the AuthUsers table contained:

usernameinstitutionpassword
admin0foy6TgL.HboTE
john0foKntnEF3KSXA
mary0bab.5ZXQdbvEo

The following code creates a dynamically sized table whose width matches the number of columns returned in the table AuthUsers:

<TABLE border = 1>
 <LOOP SQL="select * from AuthUsers" name="user">
  <TR>
   <LOOP NAME="column" array="user">
    <td><replace value="$column{0}"></td>
   </LOOP>
  </TR>
 </LOOP>
</TABLE>
The above code would output:

admin0foy6TgL.HboTE
john0foKntnEF3KSXA
mary0bab.5ZXQdbvEo

Note that the inner loop uses "user" as an array that generates a single column of data out of the row of data selected in the outer loop.

The first column of data of a data source is given the column name "value". For SQL statements, the columns are also given the names returned by the database. The following example does an SQL query and prints out all of the column names and values set by the query:

<SQL SQL="select * from AuthUsers" name="user">
<EVAL>
 for($key = nextkey($user,"");
     length($key) > 0;
     $key = nextkey($user,$key)){
   print("$user{".$key."} = ".$user{$key}."<br>");
 }
</EVAL>
On a mySQL database, this results in the columns "0", "1", "2", "LENGTH", "insert", "password", "username", and "value". For ORACLE, the column names returned by the database are in upper case.

Functions

Functions defined using the DEFFUN container can accept arguments, return values, and execute any SteelBlue tag. They are extremely useful in doing repetitive or even recursive tasks. You can even use functions to handle errors returned by the database (please see Database Error handlers for more information).

When a function is executed, a new environment is created in which you can declare local variables with the DECLARE command (you can also be declare local variables using the my Eval function). Please see the Eval Functions section for more information, an example of a recursive function, and how to define a function using the function Eval keyword.

Address Example

For example, the following function displays a set of input boxes to accept an address:
<DEFFUN FUNC="inputAddress($prefix)">
 <table>
  <tr>
   <td align=right>Address 1:</td>
   <assign name="$inputname" value="$prefix . 'ADDR1'" EVAL>
   <td><input type="text" name="$inputname" size=40></td>
  </tr>
  <tr>
   <td align=right>Address 2:</td>
   <assign name="$inputname" value="$prefix . 'ADDR2'" EVAL>
   <td><input type="text" name="$inputname" size=40></td>
  </tr>
  <tr>
   <td align=right>City:</td>
   <td>
    <assign name="$inputname" value="$prefix . 'CITY'" EVAL>
    <input type="text" name="$inputname" size=20>&nbsp;&nbsp;
    State:
    <assign name="$inputname" value="$prefix . 'STATE'" EVAL>
    <input type="text" name="$inputname" size=2>&nbsp;&nbsp;
    Zip:
    <assign name="$inputname" value="$prefix . 'STATE'" EVAL>
    <input type="text" name="$inputname" size=5>
   </td>
  </tr>
 </table>
</DEFFUN>

<sbform name="test" screen="welcome.sb">
 <b>Home Address:</b><br>
 <EVAL>inputAddress("HOME_");</EVAL>
 <P>
 <b>Work Address:</b><br>
 <EVAL>inputAddress("WORK_");</EVAL>
</sbform>
This results in a form that looks like:
Home Address:
Address 1:
Address 2:
City:   State:    Zip:

Work Address:
Address 1:
Address 2:
City:   State:    Zip:

Date Subtraction Example

The following function uses the dtEpoc function to return the difference in days between any two dates:
<DEFFUN FUNC="$res = dateDiff($a,$b)">
 <assign name="$res" value="dtEpoc($a) - dtEpoc($b)" EVAL>
</DEFFUN>

<replace value="dateDiff('1/1/2001','1/1/2000');" EVAL>
This prints the value "366". Please see the Eval Functions section to see this function defined in Eval using the function Eval keyword.

Database Error Handlers

By default, if there is an error caused by an SQL statement, SteelBlue interpretation continues and the error is displayed at the end of the page. However, you can create your own functions to handle errors, by defining a function using the DEFFUN container and assigning the function name to the variable $installed{handler}. (Actually, the error handler can be any function that can be called in the Eval language.)

The error handler function should have three arguments:

  1. A text string defining the error
  2. The error code from the database (if there is one)
  3. An error object-type number (for database errors, this number is "2"). This argument is provided to support different error objects in the future.

The return value of the error handler will determine what happens after its execution. If the error handler returns "0", SteelBlue interpretation will terminate as soon as the error handling function completes. Otherwise, SteelBlue interpretation will continue just after the SQL statement.

The following is an example of defining and using a database error handler:

<HTML>
 <BODY>

  <!-- Create a function to be an error handler -->
  <deffun func="$res = ERROR_HANDLER($text,$code,$type)">
   <if bool="$type == 2"><!-- DB Error Object -->
    <!-- Print out the message -->
    <P>
    <replace value="DB Error $code: $text">
   <else><!-- Unknown Error Object -->
    <replace value="Unknown error type">
    <!-- Do not continue after the error -->
    <assign name="$res" value="0">
   </if>
  </deffun>

  <!-- Install the handler -->
  <assign name="$installed{handler}" value="ERROR_HANDLER">

  <!-- Try an SQL statement that will fail -->
  <sql sql="select asdf from fdsa" name="test">

  <P>
  Hey, we survived a bad SQL statement!

 </BODY>
</HTML>
For mySQL, this created the following output:
DB Error : Error while processing SQL: select asdf from fdsa 

Hey, we survived a bad SQL statement!