locomotive.org


How to Use the Steam Language

Locomotive version 4.2

The Steam language is a powerful component of the Locomotive which allows you to generate Web pages with dynamic, context-dependent content. The Steam language is embedded directly into HTML documents. The Locomotive displays these documents by executing the Steam commands and inserting their results directly into the HTML document, thereby creating a custom Web page, which is then passed back to the Web user. Almost any kind of information from the Locomotive or the database can thus be inserted into a Web page. Not only that, but the Steam language has a sophisticated set of features for configuring Web pages, such as if-then-else conditions, variables, arithmetic and logical operators, and inserting one document into another document.

 

Basic Rules.

All Steam language commands are enclosed in square brackets, [ and ]. (To insert the bracket characters themselves into a document, you say \[ or \].) Most Steam commands, when executed, become text which can be inserted into a document. The simplest Steam command is the insertion of a variable into a document:

[variable-name]

Let's take an example. Let's say that you have the following HTML document:

<HTML>
<HEAD><TITLE> My Home Page </TITLE></HEAD>
<BODY>
Hi, this is [USERNAME]'s home page.
</BODY>
</HTML>

Now, let's say that the Locomotive knows that the value of the variable USERNAME is "Audrey". Then, the Locomotive will insert "Audrey" where it sees [USERNAME], and it will send back the following page:

<HTML>
<HEAD><TITLE> My Home Page </TITLE></HEAD>
<BODY>
Hi, this is Audrey's home page.
</BODY>
</HTML>

And now you know how to use the most common Steam command! This first step, knowing how to insert a variable into a Web document, will get you a long ways towards creating dynamic Web pages, even before you learn any other Steam command. Now, what if the variable USERNAME was not defined anywhere in your java module? In that case, no text will be inserted in the place of [USERNAME], and you would see the following instead:

<HTML>
<HEAD><TITLE> My Home Page </TITLE></HEAD>
<BODY>
Hi, this is 's home page.
</BODY>
</HTML>

(By the way, variable names in Steam can include the underscore character, '_', for example, TIME_LOGGED_IN.) Now, you might wonder: How does the Locomotive know what the value of USERNAME is? There are two ways you can tell the Locomotive about the USERNAME variable. One, you can write a Java module which plugs into the Locomotive, and you can give a value to USERNAME by calling a Java function. Two, you can use the following Steam command inside your HTML document:

[set my-variable [quote text or steam commands]]

This command means, "assign the value of what is within the [quote ...] command to the variable called my-variable." (This command must precede the use of the variable my-variable in your document.) The [quote...] command allows you to create a block of text which can be inserted into the document or into a variable. Let's take an example:

[set MESSAGE [quote The Perfect is the enemy of the Good.]]
Message of the day: <b> [MESSAGE] </b>

Would produce:

Message of the day: <b> The Perfect is the enemy of the Good. </b>
Note that the [set...] command itself does not insert any text into the document. Its only effect is to assign some text to the variable called MESSAGE. Note also that you can assign any text, with as many words as you like, to a variable using [set... [quote...]]. In fact, your text can even include other Steam commands, such as variables. For example, if you say [set HOME_PAGE [quote [USERNAME]'s Home Page]], where USERNAME contains "Katherine", then HOME_PAGE will contain "Katherine's Home Page". Later on, we'll combine [set...] with other Steam commands to produce some more sophisticated results.

In practice, the Locomotive and the modules that you write will set most of the variables that are used in a Steam document. Variables which have values, but are not referred to in a document, are simply ignored.

A surprising benefit of the [set...] command is that it allows the author of a document to pass information into the Locomotive while it is running. For example, let's say that the module you wrote needs to know who was the last person to modify a document. An easy solution is to set a variable called LAST_AUTHOR within your document:

[set LAST_AUTHOR [quote Peter]]
When the Locomotive reads your document and encounters this command, it will create the variable LAST_AUTHOR and give it a value of "Peter". If LAST_AUTHOR is already a known variable, it will be given a new value of "Peter".

As you can imagine, the [set...] command can be used to set or change configuration parameters during execution time, without needing to rewrite your program.

 

 

General Format Of Steam Commands.

Now that we've covered some examples of Steam commands, we're ready to give an overview of all Steam commands. This section lists each command and a short description of it. It can be used as a quick reference guide. The section after this one will go more into detail about each command.

The Steam language has an easy format. Except for [] and [variable], all the commands follow this pattern:

[operator expression-1 expression-2 ... expression-n]

where any of expression-1 ... expression-n may be a number, a variable name, a block of text, or a Steam command.

Here is a list of all the Steam commands.

[] This Steam command simply inserts empty text. It is usually combined with other Steam commands.
\[, \], or \(any character) The backslash character, '\', tells Steam to interpret the next letter, no matter what it is, as text, not as a part of a Steam command.
[variable] Inserts the value of variable into the text. If variable is not defined, inserts nothing. Note: variable should be a word that starts with an alphabetical letter and contains only alphabetical letters, numeric digits, or the underscore character, '_'. In this Steam user manual, my variable names are all capital letters and underscores, but that's just my own style.
[set variable value] Gives a variable a value. This command does not insert any text into a document; its only effect is to set the value of a variable. If you wish to take away a variable's value, then you can set that variable's value to empty text, like this: [set my_variable[]].
[if [condition]
       [do-this-if-true] ]

    [if [condition]
       [do-this-if-true]
       [do-this-if-false] ]
If the Steam command condition results in non-empty text, then the Steam command do-this-if-true will be executed, and its result will be inserted into the document. However, if condition results in empty text, then do-this-if-false will be executed instead, and its result will be inserted. If there is no do-this-if-false part, then empty text will be inserted.
[quote text-with-steam] text-with-steam contains text of unlimited length that may contain Steam commands. The Steam commands are executed and inserted into the text, and the result is inserted directly into the document. Except for the [loop...] and [table...] commands (see below), [quote...] is the ONLY Steam command which allows you to insert blocks of literal text and/or a series of other Steam commands. Therefore, it can also be used to group lots of Steam commands into one block, which can be used within [if ...] commands.
[load document-full-path] This inserts the contents of a document, located at document-root/document-full-path, into the current document. (You specify document-root in the Locomotive configuration file)
[load document-full-path noeval] This inserts the contents of the document at document-root/document-full-path into the current document, but without interpreting anything between square brackets, [...], as Steam commands.

In the following mathematical and logical operations, numeric-expr can be either a number (such as 25), a variable name (such as NUM_USERS), or a Steam command.

[+ numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
[- numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
[* numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
[/ numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
These are arithmetic operators which operate on all the numeric-exprs which follow them. The result is inserted as text. For '-', you subtract numeric-expr-2, ... numeric-expr-n from numeric-expr-1 to get the result. For '/', you divide numeric-expr-1 by each of numeric-expr-2, ... numeric-expr-n to get the result. You need at least two numeric-exprs for these operations, but you can have as many as you want.
[= expression-1 expression-2 ... expression-n]
[eq expression-1 expression-2 ... expression-n]
[neq expression-1 expression-2 ... expression-n]
[> numeric-expr-1 numeric-expr-2]
[>= numeric-expr-1 numeric-expr-2]
[< numeric-expr-1 numeric-expr-2]
[<= numeric-expr-1 numeric-expr-2]
These are comparison operators which operate on numeric-exprs. neq, eq, and = also operate on text. (eq and = are the same operator; choose your preference) If the comparison is true, then the word "true" is inserted, else no text is inserted. These are usually used as the condition in the [if...] command. neq is the same as [not [eq...] ].
[AND expression-1 expression-2 ... expression-n]
[OR expression-1 expression-2 ... expression-n]
[NOT expression-1 expression-2 ... expression-n]
These are logical operators which combine the results of comparisons. AND inserts "true" if all the expressions are non-empty text. OR inserts "true" if any of them are not empty. NOT inserts "true" only if all of the expressions are empty text.

The following commands operate on arrays, tables, or iterating over a loop:

[colname
      array-variable
      index-of-an-item]
If you have defined an array within your java module in the Locomotive, then you can access any single item within that array using this command.
[colname
      array-variable]
This command is a variant of the previous command, except that this command is used only within [loop...] and [table...] commands. This command is equivalent to: [colname array-variable [loop_index]].
[loop
      number-of-times
      text-to-repeat]
This command allows you to insert text-to-repeat (text which may contain Steam commands) for number-of-times times. Within this command, you can use either form of the [colname...] command. While iterating through a [loop...] command, the Steam variable loop_index is automatically set to the current loop counter, starting at 0.
[table
      table-variable
      text-to-repeat]
If you have defined a SortableTable within your java module, then you can use this command to iterate over each row of your SortableTable and insert text-to-repeat (text which may contain Steam commands) for each row. Within this command, you can use either form of the [colname...] command. While iterating through a [table...] command, the Steam variable loop_index is automatically set to the current row counter, starting at 0. During iteration, you can refer to the item in the table at: (row = loop_index, column = your-colname) within your SortableTable by saying [colname your-colname].

 

 

More Detailed Explanation Of Each Command

Command: [if [condition] [do-this-if-true] ]
[if [condition] [do-this-if-true] [ do-this-if-false] ]

The [if...] command allows you to insert different text depending on some condition you specify. It behaves in the same way as an if-then or an if-then-else construct in most programming languages. That are two variations of [if...] commands, the if-then form and the if-then-else form. Here is an example of the if-then form:

[if [USERNAME] [quote Hi, [USERNAME]!]]

If USERNAME = "Max", then the above statement would be interpreted as:

[if Max [quote Hi, Max!]]

Because the condition, [USERNAME], is "Max", a non-empty text, the do-this-if-true part is executed, and you would see:

Hi, Max!

The [quote...] command is often used in combination with [if...] commands, to insert blocks of text if a condition is true or false. Notice how [USERNAME] was part of the [quote...] command. You can put Steam commands within Steam commands, within Steam commands, etc. -- there is no limit on the depth of containment. (Just don't forget that for every open bracket [, you need a closing bracket ]!)

Now, let's look at an example of the if-then-else form:

[if [> NUMBER_OF_USERS 10]
[set NUMBER_OF_USERS 0]
[set NUMBER_OF_USERS [+ 1 NUMBER_OF_USERS]]
]

In this example, the [if ...] command spans several lines. In fact, any Steam command can span any number of lines. The purpose of the [if...] statement is to increase the value of NUMBER_OF_USERS by one. However, if NUMBER_OF_USERS is greater than 10, then we want to reset it to zero. Notice that in the statement:

[set NUMBER_OF_USERS [+ 1 NUMBER_OF_USERS]]

NUMBER_OF_USERS is being assigned a value which depends on the value of NUMBER_OF_USERS itself. Don't worry, this is okay. It acts the same as this common statement in most programming languages:

NUMBER_OF_USERS = NUMBER_OF_USERS + 1.

What if NUMBER_OF_USERS were not a number? This is an interesting question. If NUMBER_OF_USERS did not exist at all, or NUMBER_OF_USERS contained text which was not a number, then in all arithmetic operations, it would be treated as if it had a value of zero. However, all comparison operations involving NUMBER_OF_USERS would return false. In other words, make sure that NUMBER_OF_USERS is a number before using it in a context requiring a number.

Now, we're going to do something slightly funky and show you a different way to say the previous command. Hopefully, this will show you how flexible Steam can be, and also bend your mind a little bit! Let's have some fun.

[set NUMBER_OF_USERS 
[if [> NUMBER_OF_USERS 10]
0
[+ 1 NUMBER_OF_USERS]
]
]

If you think about it, this accomplishes exactly the same thing. If NUMBER_OF_USERS is greater than 10, then the [if...] will return zero, else it will return NUMBER_OF_USERS + 1. Notice that we did not have to put '0' around brackets, [ ]. That's because it's just a number. In fact, if we had said [0] instead, then the Locomotive would look for a variable named '0' -- which probably does not exist!


Command: [quote text-with-steam]
Within Steam commands, there is often a need to insert blocks of text. The [quote...] command allows you to do that. You can also insert Steam commands within the [quote...] command. There are examples of [quote...] in the [if...] command description.


Command: [load document-full-path]
Often, you may have a set of standard headers and footers that are included in many different Web pages. The [load...] command allows you to load the contents of another document into the current document. Here's an example:
[load admin/users/edit-users.steam]

If you specified a document root of "/usr/local/Locomotive" in your Locomotive configuration file, then this command will read and insert the contents of the document residing at: /usr/local/Locomotive/admin/users/edit-users.steam. If that document has [set...] commands which assign values to some variables, then, after the [load ...] command, those variables will now contain those values. In other words, the Locomotive treats the current document exactly the same as if it contained the loaded document.


Command: [load document-full-path noeval]
This is simply a variant of the [load ...] command which does not interpret things within brackets [...] as Steam commands, so that you can include special languages, such as javascript (javascript programs use brackets for accessing arrays) within your HTML documents without needing to escape every single '[' and ']'. Note that, therefore, any Steam commands within the document will not be evaluated; they will be passed back to the browser "as is".


Command: [+ numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
[- numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
[* numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
[/ numeric-expr-1 numeric-expr-2 ... numeric-expr-n]
These arithmetic commands add, subtract, multiply, and divide numeric expressions and then insert the resulting number as text into the document. The numeric expressions which they operate on can be numbers, variables, or Steam commands which generate numbers. Let's take an example of this:
[set SECONDS_SINCE_MIDNIGHT
[+ [* 60 60 HOUR]
[* 60 MINUTES]
]
]

For this example, let's say that HOUR = 9 and MINUTES = 45. Then, SECONDS_SINCE_MIDNIGHT would get the value (9 * 60 * 60) + (45 * 60), or 35100.

Note that you can use the names of variables without using brackets, [ ], within arithmetic Steam commands, whereas you cannot do this inside other Steam commands, such as [set...] or [quote...]. For example, if you said: [set MY_NAME USERNAME], then MY_NAME would get the value of the text, "USERNAME" -- it would NOT get the value within the variable USERNAME unless you said: [set MY_NAME [USERNAME]]. The reason for this difference is because [set...] is more often used to assign blocks of text to a variable, and seldom used to assign one variable to another, while arithmetic and logical operators most often operate on single variables. Keep this difference in mind.


Command: [= expression-1 expression-2 ... expression-n]
[eq expression-1 expression-2 ... expression-n]
[neq expression-1 expression-2 ... expression-n]
[> numeric-expr-1 numeric-expr-2]
[>= numeric-expr-1 numeric-expr-2]
[< numeric-expr-1 numeric-expr-2]
[<= numeric-expr-1 numeric-expr-2]
Often, within your [if ...] commands, you need to compare two numbers or two numeric quantities. These Steam commands allow you to do that. Here are the meanings of each of the commands:
= or eq tells whether a number of expressions evaluate to the same result. (Note: = and eq can operate on numbers, text, or Steam commands.)
neq tells whether at least one expression among a number of expressions evaluates to a different result from all the other expressions. Equivalent to [not [eq...]]. Can operate on numbers, text, or Steam commands.
> tells whether the first numeric expression is greater than the second.
>= tells whether the first numeric expression is greater than or equal to the second.
< tells whether the first numeric expression is less than the second.
<= tells whether the first numeric expression is less than or equal to the second.
Here are some examples:
[if [= USERNAME FIRST_NAME EMAIL_NAME]
[quote Your username is the same as your first name and email name.]]

[if [neq FIRST_NAME EMAIL_NAME] [set USERNAME EMAIL_NAME]]

[if [neq USERNAME FIRST_NAME EMAIL_NAME]
[quote Among your username, first name, and email name, one of these
is different from the others.]]

[if [> NUMBER_OF_USERS 10] [set NUMBER_OF_USERS 0]]

In the last example, what if NUMBER_OF_USERS were not a number? This is an interesting question. If NUMBER_OF_USERS did not exist at all, or NUMBER_OF_USERS contained text which was not a number, then in all arithmetic operations, it would be treated as if it had a value of zero. However, all comparison operations involving NUMBER_OF_USERS would return false. In other words, make sure that NUMBER_OF_USERS is a number before using it in a context requiring a number.

A simple way to test whether NUMBER_OF_USERS is a number is the following:

[if [>= NUMBER_OF_USERS NUMBER_OF_USERS]
[quote The variable NUMBER_OF_USERS is a number with the value 
[NUMBER_OF_USERS].]]

This test works because if NUMBER_OF_USERS is a number, then [>= NUMBER_OF_USERS NUMBER_OF_USERS] will always return true, because any number is greater than or equal to itself. However, if it is not a number, then any arithmetic comparison with it will return false. (Note: we can't use '=' in place of '>=', because '=' can compare text expressions.)

Of course, if you don't know whether NUMBER_OF_USERS is even a variable containing a value, you can simply say:

[if [NUMBER_OF_USERS]
[quote The variable NUMBER_OF_USERS contains the value
[NUMBER_OF_USERS].]]

Command: [AND expression-1 expression-2 ... expression-n]
[OR expression-1 expression-2 ... expression-n]
[NOT expression-1 expression-2 ... expression-n]
These Steam commands are typically used to combine the true or false results of comparison commands. However, they can operate on any Steam command. AND inserts the text "true" if all the expressions are non-empty text. Otherwise, it will insert empty text. OR inserts "true" if any of the expressions are not empty. NOT inserts "true" only if all of the expressions are empty text.

There is no limit on the number of expressions that can be included inside each command. Here are some examples:

[if [AND [>= NUMBER_OF_USERS MIN_NUMBER_OF_USERS]
[<= NUMBER_OF_USERS MAX_NUMBER_OF_USERS]
[USERNAME]
[quote Congratulations, [USERNAME], you can log on.]]

Notice that [USERNAME] was one of the expressions in the [AND...] command. In that context, the [AND...] command will treat [USERNAME] as a "true" statement if the variable USERNAME has a non-empty text value; if not, then the entire [AND...] command will return a "false" result -- in other words, empty text.

[if [NOT [USERNAME] [FIRST_NAME] [LAST_NAME]]
[quote Login Name: <INPUT NAME="USERNAME" LENGTH=20>]]

In this example, if none of the variables USERNAME, FIRST_NAME, or LAST_NAME has a value, then an HTML text field will be displayed.

The [NOT ...] operator is useful in combination with the [= ...] operator to determine whether a group of expressions all evaluate to the same text or not:

[if [NOT [= USERNAME LOGIN_NAME FIRST_NAME]]
[quote [set USERNAME [FIRST_NAME]]
[set LOGIN_NAME [FIRST_NAME]]
]
]

In that last example, we used the [quote ...] command not to insert any text, but rather, to be able to run two commands within one Steam command.

By the way, you can "nest" these commands within each other, just like any other Steam Command:

[if 
[OR 
[AND [= SOCRATES MAN] [= MAN MORTAL]]
[SOCRATES_DIED]
[NOT SOCRATES_LIVES]
]
[quote I'm sorry, Socrates is mortal.] ]


Command: [colname array-variable index-of-an-item]

This command allows you to get the value of any item in an array which you have defined in your Java module. Let's take an example. Let's say that you've defined an array of Strings called "usernames" in your Java module:

String usernames[] = new String[4];
usernames [0] = "Matthew";
usernames [1] = "Mark";
usernames [2] = "John";
usernames [3] = "Ringo";
...
hd.subs.put ("usernames", usernames);

Then, within your Steam document, [colname usernames 2] will generate "John". Notice that you can use either a number or a Steam command for the index argument. For example, let's say that the variable WHICH_USER has the value "3". Then, [colname usernames [WHICH_USER]] will generate "Ringo". However, note that you must specify the name of the array directly; you cannot use a Steam command to generate it. For example, you can't say [colname [WHICH_ARRAY] 3].


Command: [colname array-variable]

This command is a variant of the previous command, except that this command is used only within [loop...] and [table...] commands. Its index argument is replaced by the value of the variable loop_index that is set during each cycle of the [loop...] or [table...] command. In other words, this command is equivalent to:

[colname array-variable [loop_index]].

There are examples in the [loop...] and [table...] commands.


Command: [loop number-of-times text-to-insert-each-time]

Sometimes, you may want to repeat some text or Steam commands a number of times in your Steam document. This command allows you to do that. The argument, number-of-times, can be either a number or a Steam command which generates a number. The argument, text-to-insert-each-time, is text interspersed with any number of Steam commands, including either form of the [colname...] command. While iterating through a [loop...] command, the Steam variable loop_index is automatically set to the current loop counter, starting at 0. Here's an example of this command involving loop_index:

[loop 4 Counter = [loop_index]. ]

Which generates:

Counter = 0.
Counter = 1.
Counter = 2.
Counter = 3.

Here's an example which includes the special use of the [colname column_name] Command:

[loop [NUMBER_OF_USERS]
The user at [loop_index] is named "[colname usernames]".<BR>
]

which generates:

The user at 0 is named "Mark".<BR>
The user at 1 is named "Matthew".<BR>
The user at 2 is named "John".<BR>
The user at 3 is named "Ringo".<BR>

 


Command: [table table-variable  text-to-insert-for-each-row]

Recently, we added some very powerful functionality to the Locomotive. This command is one which gives us pride. In the Locomotive, there is a Java class called SortableTable which can store the results of an SQL query in a table that can be sorted. SortableTable gives you the ability to customize the way a database table looks in your document. If a SortableTable (see SortableTable.java) is defined within your java module, then you can name it as the first argument in the [table ...] command to iterate over each row of that SortableTable and insert some text (which may include Steam commands) for each row.

Within this command, you can use either form of the [colname...] command. While iterating through a [table...] command, the Steam variable loop_index is automatically set to the current row counter, starting at 0. During iteration, you can refer to the item in the table at: (row = loop_index, column = your-column) within your SortableTable by saying: [colname your-column], or by saying: [colname your-column [loop_index]].

The [table ...] command is just like a [loop...] command that has several [colname...] commands within it, except that all the names of the columns belong to a single SortableTable. Let's take an example.

Let's say that you've defined a SortableTable in your Java module called "users_messages". This SortableTable has two columns named "usernames" and "number_of_messages". The contents of this table are:

row:usernames:number_of_messages:
0Sterling54
1Ben23
2Chris12
3Thede35

Now, consider the following block of Steam within a document:

<table>
[table users_messages
<TR><TD> The user at row [loop_index] 
is named "[colname usernames]".</TD>
<TD> [colname usernames] has 
[if [>= [colname number_of_messages [loop_index]]
[colname number_of_messages 3]]
[quote as many or more]
[quote fewer]
]
messages than [colname usernames 3].</TD></TR>
]
</table>

When this block is interpreted, it will generate a table which looks like:

The user at row 0 is named "Sterling". Sterling has as many or more messages than Thede.
The user at row 1 is named "Ben". Ben has fewer messages than Thede.
The user at row 2 is named "Chris". Chris has fewer messages than Thede.
The user at row 3 is named "Thede". Thede has as many or more messages than Thede.

Here are some things to take note of in this example. First, note that [colname usernames] means, "insert the value of the column named 'usernames' at the row loop_index, from the current table". If there is already an array named "usernames", that array will not be used; this "usernames" refers to the column in the users_messages SortableTable. Because of this behavior, be careful in naming your arrays and columns, so you don't get confused.

Second, note that we are allowed to use the "absolute location" form of the [colname...] command within the [table...] command. That is, [colname usernames 3] refers to the value at the column "usernames" at row 3. This feature therefore allows you to use the value of the previous or next item in the column, by saying [colname usernames [- loop_index 1]] or [colname usernames [+ loop index 1]]. This gives you great flexibility in referring to other items within the table.

 

 

Tips for Debugging HTML Steam Commands

Here are a few tips about how you can test and debug your HTML templates while you're testing your features.

  1. Turn off caching in your browser. You need to do this in order to make sure that the pages you're receiving from the web server/Locomotive are the latest ones.
  2. Make the Locomotive always reload HTML templates. Normally, the Locomotive does not load the HTML template files every time, but caches them. But when you're testing, you'll want the Locomotive to reload templates every time, by setting the variable LOCO_CHECK_NEW_TEMPLATE_FREQ to the value '1' in the las.conf file:
  3.     LOCO_CHECK_NEW_TEMPLATE_FREQ            1
    When you run a Locomotive on the live site, you can make this value higher, in order to save time, because you know that the templates won't change very often on the live site. (There is also a way to force the Locomotive to reload all templates once, whether they've changed or not; see The Locomotive Administrators Guide.)
  4. Try displaying an HTML template as a regular HTML document. If a particular HTML template is not displaying correctly, try putting a copy of it in the web server's document root directory and displaying it. This can help you check that your HTML Tags, especially in Tables, are "balanced" and are not the source of the problem.
  5. Balance your brackets [] for Steam commands. If the HTML is correct, and you're having problems with the Steam commands, first check to make sure that all of your brackets, [], are balanced.
  6. Break down complex Steam commands into their parts. If your Steam commands are complex, try copying some smaller components within them outside in order to display their values. In other words, isolate the problem by process of elimination. Remember that every Steam command has a text value if you display it, even the logic commands such as [and/or/not...] (whose values are either "true" or "", the empty string). Here's an example. We'll take out one sub-clause within the following Steam command:
  7.     [if [and [USERNAME] [= USERS_AGE 21]] 
          [quote Hi, [USERNAME], you're legal!! :^)]]
    You can check whether USERS_AGE = 21 directly by saying [= USERS_AGE 21], which will evaluate to "true" if it is true. Better yet, we can simply find out the value of the USERS_AGE variable by saying [USERS_AGE].
  8. Make sure that the HTML templates are really being displayed. If you're getting a basic "Server Error" directly from the web server, not from any of the Handlers in the Locomotive, chances are that your code failed to display any HTML content, either because of a bug in your code so that it skips displaying any pages, or because the path of your HTML template is incorrect. Check both possibilities, as well as the permissions bits of your HTML template file and the directories which contain it.

 

 

Variables which the Locomotive Already Defines

The Locomotive defines the following variables, which you can use in any of your Steam documents by calling the StandardInsertions.insertAll() command:
DATE The date today, in the form: Mmm dd, yyyy. For example: Mar 23, 1998.
TIME The time right now, in the form: hh:mm AM/PM. For example: 9:45 P.M.
USERNAME If the user has logged in with a password, this variable will contain his or her username; otherwise, this variable is not set.
USERID Each user in the Locomotive system has a unique numeric userid. If the user has logged in, then this is their userid. Otherwise, if the user is anonymous, this is 0.
SESSIONID The Locomotive uses browser cookies to track a user's single, continuous use, or "session". This variable refers to the current session's ID.
LOCOMOTIVE_SERVER_VERSION This is the version number of the current Locomotive, which is useful for things like tracking bugs.
SYSTEM_TAG This is the main Locomotive identification tag. It's pulled from the SYSTEM_TAG config variable set in the loco.conf file.
SYSTEM_INSTANCE_ID This is the unique ID associated with this particular Locomotive. Taken from the config variable SYSTEM_INSTANCE_ID, set in loco.conf