Language Reference Manual
Version 2.1
WebKeystone was written to satisfy a need for a CGI-like
language that was easy to learn, easy to write in, and powerful
enough to do a variety of common CGI-script chores. It had to
be both tightly coupled with http requests and secure
enough so that applications written in it could be trusted
not only to not impinge on other applications and the system,
but also to not overuse system resources. It also needed to
be able to account for the resources that are used, so that
they could be both allocated and billed for correctly.
WebKeystone allows a site to give subscribers access to a
powerful scripting language with confidence in the security of the system
and the ability to bill for resources used.
The WebKeystone programming language is called "WebKeyscript".
WebKeyscript is a web scripting language based heavily upon the Python programming
language. It is
considerably simpler to use for web-based applications and has more security, but is not as
general purpose as Python. If the full features of Python are needed,
there is an escape mechanism that can be installed in order to
"hook" or "wrap" a Python routine. The wrapped Python routine
then has access to the full spectrum of system resources available
to a user.
WebKeyscript provides a secure and convenient operating environment.
It also provides access to persistent and temporary databases
that can be used to build applications such as shopping carts,
which need to preserve state in an Internet environment that was
designed to be stateless. It is our observation that most
applications can be built in WebKeystone alone without access
to other programs. This includes applications as complex as
full-scale bookstores based upon multiple catalogues and Web pages
of descriptions complete with a shopping cart that maintains state,
automatic billing, invoice calculations and mailed confirmations,
and a search engine.
All WebKeystone forms are dynamic and can be generated and displayed
automatically. We have also built other applications including
calculators, color selection devices, and Web-based reservation
systems. WebKeystone can be mixed transparently with Java,
JavaScript, and other Internet scripting languages.
The program portion of WebKeystone is private, and cannot be
seen by anyone except the owner of the WebKeystone forms and
the System Administrator. The entry points into the system are
controlled by the form owner. It is possible, using this
technology, to easily install private Web pages on public
Web servers.
WebKeystone is invoked from an HTML form as a CGI-script. As
mentioned previously, the WebKeystone programming language is
called "WebKeyscript". Any file that invokes WebKeystone is
called a "Startfile". There is a file that contains the script
that WebKeystone executes; this file is called the "Profile".
The result of executing the Profile is a returned HTML file
which is displayed to a user; this returned file is called an
"Endfile". We call the person who wrote a particular application,
or who owns the site where the application is run, the
"form owner". We call a person who accesses WebKeystone via
the World Wide Web a "user".
The Startfile resides in a directory tree. If this
is the mechanism used to invoke WebKeystone, then this particular
file is in a webserver addressable area. All other parts of the system may be
stored in a private area, away from this public area. We call the
Startfile's directory tree, which is available to the Web server
(such as apache), the "Conventional Web Space". There is another
directory tree used by a form owner, which is where Profiles,
Endfiles, and other dynamically altered WebKeystone files are
stored. We call this directory the "WebKeystone File Space".
There may be a sub-directory tree of the WebKeystone File Space
that is used to store database objects and flat files ; we call
this the "file directory". (The system can be configured so that all
of these directories reside in different areas; however our
convention, for security and ease of administration, is to
establish the WebKeystone File Space with the file directory as a
sub-directory in a directory tree that is separate from the
Conventional Web Space.) There is a file for each owner, called the
UserID file, where the configuration information is stored in an XML format.
Within this file it is possible to designate, on a per user basis, other areas in the
server's file structure that are accessible for other purposes.
<FORM METHOD="POST" ACTION="/wkstone/webkeystone.py">
On your system this name may be different; the correct
name is available from your System Administrator. It must
follow the "ACTION=" command and must be enclosed in double
quotes.
<A HREF="http://www.webkeystone.com/wkstone/webkeystone.py?
UserID=biz_marketing&Profile=lesson1-1.prof">Go Shopping!</A>
This statement declares that the WebKeystone program resides in:
"/wkstone/webkeystone.py"
The UserID name is:
"biz_marketing"
The Profile name is:
"lesson1-1.prof"
".../wkstone/wksi/aUserId/aProfile"
"aUserId" is some UserID and "aProfile" is some
Profile. The wksi mechanism allows the UserID and the profile to be set as a part
of the URL.
<INPUT TYPE="HIDDEN" NAME="UserID" VALUE="biz_marketing">
Substitute your own UserID for "biz_marketing".
<INPUT TYPE="HIDDEN" NAME="Profile" VALUE="lesson1.prof">
Substitute your own Profile path and filename for
"lesson1.prof" (relative to your WebKeystone File Space
directory). The path may contain sub-directories
such as "/car/parts/batteries/neverready.HTML" but
may not contain references to directories above
the WebKeystone File Space directory
(ie, "../../stuff.HTML" will not work.) In addition, operating system
directory links have been disabled.
A second method of invoking a Profile is through the indirect
link mechanism:
NAME="Profile" VALUE="[shopping_cart:maIndex1.prof]
In this technique there are two UserIDs. One is given as the
UserID and the other is a linked ID, named explicitly using
the ":" mechanism in the Profile. In this example,
"shopping_cart" is the second UserID and is included in the
value setting of the Profile. The Profile maIndex1.prof in the
WebKeystone File Space owned by shopping_cart will be executed
if the permissions are correct. (In order for a user to use
this second mechanism, permission must have been granted.)
The name of the indirect UserID must appear in a list within
the direct user's UserID file. Such indirect accesses should
only be allowed by the System Administrator to trusted
applications which implement security.
<INPUT TYPE="TEXT" NAME="Email" SIZE="30">
Now you have a field called "Email" which is a variable you may
manipulate in your Profile. You could use that variable in the
"TO" line of your "MAIL" command to send a message to your user:
MAIL
SUBJECT 'My Subject'
FROM 'myEmailAddress@webkeystone.com'
TO Email
BODY 'MyMail.txt'
A list of all the form fields (variables) submitted in the form
is stored in a built-in list named CGI_FORM_KEYS.
RETURN "nextFile.html"
This returns the file named "nextFile.html" from the
WebKeystone File Space. This is an Endfile and is returned
with substitutions. The Endfile may also contain forms
that invoke WebKeystone. In fact, most complex applications
have many cross-linked forms. Endfiles are also frequently
Startfiles as well. The terms "Endfile" and "Startfile" are
functional and contextual and are not mutually exclusive.
Security is a major issue in all Internet applications. WebKeystone
was designed with security issues in mind. WebKeyscript
performs two critically different functions. The first function is an
extension of the system functions made available to a user. WebKeystone
performs many operations automatically that a CGI script has to do
procedurally. It also provides specialized functions and commands
unique to WebKeystone that make the processing of form data
quicker and more convenient and greatly reduce the development time
needed to create an interactive site. The second function that
WebKeystone performs is a limitation and restriction upon the system
environment in order to increase and enhance the security of the system.
eval, open, reload, __import__, getattr, setattr,
compile, raw_input, delattr, execfile, lambda
These are available in "hooked" programs if a form owner has
been granted access to these less secure facilities.
WebKeyscript is similar to Python, is written in Python, and is based
upon Python, but is NOT the same as Python. On one hand it provides
its own set of built-in commands and objects and functions. On the
other hand, it seriously limits and prohibits the use of many built-in
Python commands. This chapter documents
the commands WebKeyscript inherits, the commands WebKeyscript prohibits,
and the new commands WebKeyscript introduces.
There are four places where assignments to variables take place
in WebKeystone. The first is in forms in the Startfile. All
cgi-variables are declared in the forms. The second place is
during validation and conversion. Validations and conversions
are based upon suffixes of cgi-variables. During the process
of conversion new values may be assigned to these variables.
There are two places where the form writer can explicitly make
assignments to variables in WebKeystone. The first is in the
Profile and the second is in the Endfile. Assignments may
occur throughout the Profile; the Endfile may begin with an
optional header in which assignments can be evaluated. The
header is separated from the rest of the Endfile by
a line beginning with the two characters "#-". The header is
executed and stripped from the Endfile when it is invoked by the
RETURN statement. Any file in which substitutions may be performed
may contain assignments in a header. This includes files mailed
and specified in the BODY command or the ATTACH command. These
assignments are only performed if the file is loaded with
substitutions. This header feature was added in order to make
certain kinds of processing, in particular the display of lists,
convenient. It keeps the formatting for the list close to where
the list will be displayed, but hides this processing information
from users.
Let a = 'abcd1234xyz'
The "==" sign means is equal to. All of the following
statements with respect to "a" are true.
a[0] == 'a'
a[10] == 'z'
a[1:3] == 'bc'
a[1:] == 'bcd1234xyz'
a[:3] == 'abc'
a[1:-1] == 'bcd123xy'
a[:-3] == 'abcd1234'
a[-3:] == 'xyz'
The symbol "+" is used for concatenating strings. Using the
value for "a" above:
a[1:5] + a[-3:] == 'bcd1xyz'
Other operations are also possible and WebKeystone imports the
string component of Python which can be used with the prefix
"f.string.".
x = ['abc',77,['xyz']]
x is a list containing three items: the string 'abc', the integer
77, and the list containing one item ['xyz']. Similar to strings,
lists may be sliced and combined using the "+" symbol.
Examples:
x[0] == 'abc'
x[1] == 77
x[:2] == ['abc',77]
[x[0],x[2][0]] == ['abc','xyz']
x[2] + x[2] == ['xyz','xyz']
d = {'abc':1234, 7:'xyz'}
The first pair is indexed by a string and returns an integer.
The second pair is indexed by an integer and returns a string.
d['abc'] == 1234
d[7] == 'xyz'
Dictionaries may also contain arbitrary Python objects as values
and can be indexed with almost any Python object. In order
to create a list of the keys of a WebKeystone dictionary,
you can use the keys() method:
d.keys() == ['abc',7]
d.values() == [1234,'xyz']
d.items() == [('abc',1234),(7,'xyz')]
d.items() == [('abc',1234),(7,'xyz')]
are tuples. Tuples behave like lists for slicing and indexing,
but are immutable and cannot be added to or deleted from, nor
can their elements be changed. Tuples do not have as much use
in WebKeystone as lists do, but they can be created and they
may occur in certain special applications. Tuples are more
obscure in WebKeystone than they are in Python.
+ - * ** / %
<< >> & | ^ ~
< > <= >= == != <>
The comparison operators <> and != are alternate spellings
of the same operator; != is the preferred spelling; <>
is obsolete.
These are the four arithmetic operators:
+ - * /
They also maintain their Python meanings for other
objects. + is the concatenation operator for strings and
* will operate on a string with a number to cause a repetition.
% is modulo division but will also operate on a string for
substitution and is used similarly to the C string formatting
token.
These are bitwise operators and their use is obscure in WebKeystone:
<< >> & | ^ ~
These are comparison operators:
< > <= >= == != <>
In addition, the boolean-like operators "and", "or" and "not"
are inherited from Python.
Several components are also imported from Python.
In particular all the functions and variables in the
following Python components can be used with the exception
of f.time.sleep(<seconds>) command which is not available.
"string", "math", "random", "time", "cmath", "convert", "validate".
They are used in WebKeystone by preceding their name with an
"f.". For instance:
f.string.join(['The','small','pig']) == 'The small pig'
f.string.atof('.2999') == 0.2999
Other available functions of these components can be found in
the Python Library Reference Manual. Convert and validate
are covered herein because they are WebKeyscript-specific.
Built-in Python functions that can be executed from
WebKeystone include:
hex, min, abs, int, len, round, max, range,
map, divmod, float, type, list, long, str,
ord, chr, coerce, oct, cmp, reduce, filter,
pow, complex, apply
%<variable> = <unquoted string>
The unquoted string starts after the white space following the
equals (=) sign and ends at the last non-white space character
on the line. Any string of the form %(<variable>)s in the string
on the right-hand side of the equals sign is replaced with the
value of the variable in the string. All other symbols (except
the continuation line character) are treated verbatim. The
leading and trailing white space is stripped. The continuation
line character plus (+) is honored.
<variable> = <WebKeystone expression>
DEL <variable>
will change
the value of <variable> and pop the stack unless there are
no values on the stack, in which case it will delete the
variable.
IF <expression>
<indented block>
The indented block is executed only if the expression is true;
otherwise the command following the indented block is
executed.
ELSE
<indented block>
The indented block is executed only if the previous expression
executed was an IF or ELIF instruction and the result
of the evaluation of the expression was false.
ELIF <expression>
<indented block>
The indented block is executed only if the previous executed
expression was an IF or ELIF expression and the result of
the evaluated previous expression following the IF or ELIF
was false and the result of evaluating the expression
following the current ELIF expression is true.
TRY
<indented block>
ELSE
<indented block>
The indented block is executed. If a WebKeystone error
occurs in the indented block, control is passed to the
indented block following the ELSE statement. The complete
exception list with formatting is contained in the
variable 'exception' if the indented exception block
following the ELSE statement is executed. The format
of this message in 'exception' is tentative and should
not be depended upon.
RETURN <nosub filename>
or
RETURN <mime type> <numbs filename>
or
RETURN
<mime type> <nosub filename>
.
.
.
A few commands, such as the CUSTOM command and the DOWNLOAD
command, use an additional indentation.
The expression following RETURN is evaluated and the result
is used as the name of a file which is returned to the user.
RETURN <filename>
is equivalent to
RETURN TEXT_HTML <filename>
RETURN is the last command executed in the Profile and terminates
the processing. The expression may optionally be preceded
by NOSUB which will prevent substitutions from taking place.
NOSUB also suppresses evaluation of all code in an Endfile. Code
in an endfile can occur in two places. The first is at the beginning
of the file. Here it is separated from the HTML portion of the Endfile
by a comment which begins with "#-". The second is within a substitution
tag. NOSUB will suppress the evaluation of WebKeyscript code
in both places. The code at the beginning of a returned
file (which is separated from the rest of the file by the "#-"
comment line) is always stripped and never returned to
an end user over the web. When NOSUB is not present
in the RETURN statement, the WebKeyscript is executed
before substitutions are made. When NOSUB is present,
the WebKeyscript is stripped but not executed, and
the remainder of the file is returned verbatum.
RETURN uses a mime text/html type to return a file.
Several other mime types are available and can be combined
in a "Content-Type: multipart/x-mixed-replace" wrapper by including them
indented after the use of the RETURN MULTIPART command. Currently a RETURN with
no arguments defaults to a RETURN MULTIPART. The following may occur after a RETURN
command, or be combined in a RETURN MULTIPART command:
TEXT_HTML which is 'Content-type: text/html; charset=us-ascii'
TEXT_PLAIN which is 'Content-type: text/plain; charset=us-ascii'
DOWNLOAD which is 'Content-Type: application/octet-stream'
IMAGE experimental
CUSTOM experimental
Here is an example using mime types in a MULTIPART RETURN:
RETURN MULTIPART
TEXT_HTML
"Title.htm"
DOWNLOAD
"Dummy.txt"
TEXT_PLAIN
RAW "You are done downloading"
Not all browsers support these types.
FOR <variable> IN <list>
<indented block>
The variable is assigned to values in the list one at a time.
For each new value of the variable the indented block is
executed once.
CONTINUE
Causes the next value in the enclosing FOR loop to be
evaluated and the indented block to be reentered at the
top.
BREAK
Causes the enclosing FOR loop to be terminated and the
next expression following the indented block to be executed.
EXITWITH <Webkeystone expression>
EXITWITH is used with DEFINE to return a value from a function.
DEFINE <function name>,<dummy argument list> <WebKeystone code>
The define command declares a block of code as a function.
The indented code is executed every time the <function name> is referenced
in an expression. All variables used within the defined block are global except
those passed in the <dummy argument list>. An example of the declaration
of a function that finds the average of two numbers is:
DEFINE average2,value1,value2
EXITWITH (value1+value2)/2
An example of code that increments a variable and returns
the incremented value is:
XXX = 1
DEFINE inc_XXX
XXX = XXX+1
EXITWITH XXX
An example of a recursive function is:
DEFINE factorial,x
IF x==0
EXITWITH 1
ELSE
EXITWITH x*factorial(x-1)
After being defined, the function factorial() can be used in any
WebKeystone expression.
CALL <Profile name>
or
CALL <Profile name>[,<variable name>]*
When followed by a Profile name, this command "calls"
the Profile. The Profile name may optionally be followed
by a comma and a list of variables. These variables
are pushed and recovered after the routine returns.
All other variables are treated as globals.
Note that both the Profile name and the pushed variable
names MUST be in quotes, or be an expression that
evaluates to the name.
When a Profile is called, it is entered with the
same name space as the calling Profile. If it
finishes without performing a RETURN command, control
is returned to the calling Profile. All variables
in the variable list are popped back to their old
values.
These two command sets are equivalent:
A => A
B => B
CALL "recurse.prof"
DELETE A
DELETE B
and:
CALL "recurse.prof","A","B"
LOCK <lock-name>
<indented instructions>
The block of code is locked with respect to all
other processes and threads. Only one thread in one
process can execute a piece of code with the lock
named <lock-name>. The lock is maintained as long
as the indented instructions are executing and is
released when they are exited. The lock has no effect
upon the current thread or process. It is only
intended to keep others out.
'My dog is a %(dogtype)s.'
Would evaluate to:
'My dog is a mutt.'
when loaded from an Endfile or when included in an assignment
such as:
%dogline = My dog is a %(dogtype)s.
The leading % is required in this assignment and the string
is NOT included in quotes. We will call strings of the
form '%(<variable name>)s' tags. This particular tag ends
with an "s". WebKeystone provides for a number of different
formatting endings. These formatting endings are taken
from string formats in the C language. Formats allow the positioning
of a string in a fixed length
field. To justify strings, use space numbers with the tags.
This will right justify in 13 spaces::
%(<variable name>)13s
This will left justify in 13 spaces:
%(<variable name>)-13s
The first number following the ")" and preceding the
"s" will set the number of spaces in the field. If the
number is negative left justification will be used.
If the length of the variable being substituted is greater
than the field length, the field length is extended.
Use tags to force truncation. This will right justify in 13 spaces
and will truncate if the length of the variable is
over 13 spaces:
%(<variable name>)13.13s
This will left justify in 13 spaces and will truncate if
the length of the variable is over 13 spaces:
%(<variable name>)-13.13s
The truncation size can be different than the justification.
There are several other formatting options, which will not be
covered here. Refer to a Python reference manual to get these
alternative codes.
template = '<LI>%(1)s'
be an assignment in the header of our Endfile. Let:
<UL>%(aList,template)s</UL>
occur somewhere in the body of the Endfile. When the
Endfile is loaded and substituted into, each item in
the list "aList" will be preceded by the <LI> tag. Let:
aList = ['dog','cat','bird']
In the Endfile <UL>%(aList,template)s</UL> will be replaced
with:
<UL><LI>dog<LI>cat<LI>bird</UL>
Which will produce the desired list.
Multiple lists can be combined in a single substitution.
The lists are comma-separated. The general format is:
%(<list1>,<list2>,...<list>,<template>)s
If a tag contains n lists, then the n tags, %(1)s, %(2)s ...
%(n) can be used in a template to indicate an item from
<list1>, <list2> and <list> respectively. The lists
should all be the same length, although WebKeystone will
lengthen those that are shorter. The tag %(0)s can also
be used to indicate which element in the list is being
processed.
Example:
list1 = ['Bob','Mary','Bill']
list2 = ['dog','cat','bird']
template = '%(0)s %(1)s owns a %(2)s. \n'
In the file returned by a BODY command is the line:
%(list1,list2,template)s
which is replaced in the mailed item by:
1. Bob owns a dog.
2. Mary owns a cat.
3. Bill owns a bird.
%(<tree>,<node-template>)s
Like list templates,
node-templates can contain the substitution variables
%(0)s, %(1)s, %(2)s ... Within the node-template, these values
are replaced with %(0)s--The depth in the tree of the node,
%(1)s--The Unique Id of the node. %(2)s--The parent of the
node. %(3)s--The first piece of data associated with the
node (if it exists). %(4)s--The second piece of data associated
with the node, and so on.
Frequently one will want to lay out a tree in an outline
format with indentations at each branch in the tree. Directory
trees are frequently laid out this way.
In order to do this the indentation template is provided.
The substitution would have the form:
%(<tree>,<node-template>,<indentation>)s
The indentation templates is repeated 0 times for nodes with
no parents, once for nodes with parents that have no parents,
and at the nth level in the tree it is repeated n-1 times.
It is immediately followed by the node-template for the node.
The indentation-template, like the node template, can contain
substitution variables, although the usage is less common, and
frequently all the indentation-template will contain is spaces.
There are three other templates: A begin-template, and
end-template, and a beg-end-indentation template.
The begin-template and the end-template are used before and
after a new level of indentation. If not specified, the
beg-end-indentation defaults to the same as the indentation.
It is used in the same manner, as a means of indenting based
upon the depth of the nodes in the tree.
%(missingVal,oldv(missingVal),'%(1)s failed. %(2)s is wrong!')
It is safest to put evaluated strings and complex expressions
in the prologue. This statement would been better implemented as:
template = '%(1)s failed. %(2)s is wrong!'
#-----------------------------------------
...
%(missingVal,oldv(missingVal),template)
Evaluation will fail if the parentheses do not match. The
following is a legal Python expression that fails in a WebKeystone
substitution:
%(")")s
`233` == '233'
'The Number ' + `7 + 8` == 'The Number 15'
This is shorthand for the function str() which works the same:
str(233) == '233'
'The Number ' + str(7 + 8) == 'The Number 15'
MAIL
<indented block>
This command has no argument and starts collecting information
for a mailing. Only the commands described in this section
can be used in the indented block following a MAIL command.
All other WebKeystone commands are illegal here.
SUBJECT <expression>
The expression is evaluated as a string and used as the
subject line of the email. Only one of these should appear
in the indented block.
FROM <expression>
The expression is evaluated as a string and used as the
from address of the mail. Only one of these should appear
in the indented block.
TO <expression>
The expression is evaluated as a string and used as the
to address of the mail. If the string evaluates to a
list of addresses separated by commas, the mail is sent
to the whole list. If the expression evaluates to a
list, the list is treated as a list of addresses and
the mail is sent to each address. Only one of these should
appear in the indented block.
BODY <nosub filename>
The expression following BODY (or NOSUB if it is present)
is evaluated as an expression and used as the filename
of a file to be returned as the body of the email. If
NOSUB is not present, then substitutions are done in the
file. Only one of these should appear in the indented block.
If RAW is present the string is used directly and not as
a filename.
ATTACH <nosub filename>
The expression following ATTACH (or NOSUB if it is present)
is evaluated as an expression and used as the filename
of a file to be returned as an attachment to the email. If
NOSUB is not present, then substitutions are done in the
file. Any number of these may appear in the indented block.
If RAW is present the string is used directly and not as
a filename.
ATTACH
<attachment name1>, <filename1>
<attachment name2>, <filename2>
...
The attached file is assigned the new name. The filename
may be preceded with NOSUB and RAW.
DB_READ <filename>
<variable>
...
The file <filename> contains the variable <variable>.
The file may contain multiple variables. They can each
be read by naming them in the indented list. In order
to read a variable its name must be known. The new variable
will overwrite any old variable of the same name.
DB_WRITE <filename>
<variable>
...
Multiple variables may be written to a database file
by including their names in the indented list. Each
new value of a variable already in the database overwrites
the old value in the database.
DB_DELETE <filename>
(depreciated in favor of DELETE)
Deletes the file named <filename> in reference to the
WebKeystone database area.
FF_READ <filename>
<variable>
FF_READ<filename>
<variable1>
<variable2>
A command for opening and reading a flat
file with respect to the flat file area of the system.
The contents of the file <filename> are assigned to
the variable <variable>. The old value of the
variable is destroyed.
In the second form of FF_READ, <filename>
and <variable1> have the same meaning as <filename>
and <variable>. The additional variable, <variable2>,
is assigned a timestamp value which can be used with the
FF_WRITE command (see below). If we read a file and obtain the timestamp,
then modify the file and write it back out, declaring
the timestamp, the file will fail to write if it
has been changed since that last read. The timestamp
is checked against the file; if the timestamp of
the file being written and the file being overwritten
do not match, the write is aborted.
FF_READLINES <filename>
<variable>
A function for opening and reading a list into a flat
file with respect to the flat file area of the system.
The items in the list <variable> are lines from
the file <filename>. The newline character is not stripped from the end
of each line in the list. The old value of the variable is destroyed.
FF_WRITE <filename>
<variable>
...
FF_WRITE TIMESTAMP <variable1><filename>
<variable2>
A function for writing a string to a flat file with respect
to the flat file area of the system. The string <variable>
is written on the file <filename>. Multiple indented strings
may be included. They will be concatenated together in order.
In the second form of the command, <filename>
and <variable1> have the same meaning as <filename> and <variable>
have in the first form. The variable <variable2>, declared by the TIMESTAMP command,
contains a value which is
compared against the timestamp for the file. If they do not match, an exception is thrown.
This is to ensure that when a file is rewritten after being read, it has not been changed in
the interim.
There is a side effect. If <variable1> is an actual
variable instead of an expression, and if the write succeeds, the variable
is reset with the new timestamp of the file that is written.
This allows it to be rewritten multiple times without reads
between to access the timestamp. Read FF_READ above for more
information on this variation of FF_WRITE and FF_READ.
FF_APPEND <filename> <variable>
...
This is almost identical to the FF_WRITE command. The
behavior differs only in that the string <variable> is appended to the
existing file instead of overwriting the file.
FF_WRITELINES <filename>
<variable>
...
The items of the list <variable> are concatenated together
and written to the file <filename>. No other separators are
added. Multiple indented lists may be included. They will be
concatenated together.
FF_APPENDLINES <filename> <variable>
...
This is almost identical to the FF_WRITELINES command. The
behavior differs only in that the items of the list <variable> are appended
to the existing file instead of overwriting the file.
FF_DELETE <filename>
(depreciated in favor of DELETE)
Deletes the file named <filename> in reference to the
WebKeystone flatfile area.
TF_READ <filename)>
<variable>
A function for opening and reading from a temporary
file into the name space of the Profile. The variables
read from the temporary file are the same as the variables
written to the temporary file using the TF_WRITE command.
The file name <filename> should be created using the
newid() function. Since the temporary file area is
shared among users, it is important that the file name
is both unique and secure. The newid() function implements
this. <variable> is used to indicate success or failure.
The variable will be set equal to "OK" if the file read
was successful. A temporary file that exists
for fifteen minutes without being read is automatically
destroyed. The fifteen minute limit is configurable when
installing the WebKeystone system.
TF_WRITE <filename>
<variable>
...
A command for preserving a portion of a Profile's
name space. The <variable> and all other variables
in the indented list are written to the temporary file
named <filename>. When this file is read, the variables
are restored. There are two ways to read this file.
First is the use of TF_READ and the second is the use of
the hidden field named "TPersist_".
Both TF_READ and TF_WRITE can be made more secure
by adding parameters to the file name. Any file
written with TF_WRITE <filename>,<parameter1>, ...
Must be read with the same parameter list or the read
will fail.
Currently all temporary files self-expire. The amount of
time before self-expiration is a system constant. In the default
installation a temporary file must be touched (read or written) every 15 minutes
to not self-expire. Under no circumstances can the programmer assume that
the file will be allowed to exist for more than four hours. These are WebKeystone
system-wide constants and can only be changed in any given WebKeystone installation
by changing constants in the wkConfig.py file (the file which sets all
system constants).
It is polite and saves system resources to delete
temporary files when they are no longer needed. However, this is NOT necessary, and in
many cases the programmer cannot know that the file needs deletion. In these cases the
temporary file self-expiration takes care of the situation. For instance,
in the shopper application, this is how abandoned shopping carts are
deleted.
TF_DELETE <filename>
Deletes the temporary file named <filename>.
NEWID <variable>
Creates a name of a file that is unique. It may be
used as the name of a temporary file that preserves
state. This instruction is depreciated in favor of
the newid function:
<variable> = newid()
The usage of the newid function is described in the
Chapter on WebKeystone Functions.
MERGE <variable>
<indented list of expressions>
The variable must be a dictionary in the name space. The
indented list of expressions is a list of expressions, one
to each line, that evaluate to dictionaries. Each dictionary
in the list is merged into the dictionary in the variable.
If a duplicate key occurs, the new value replaces the old
value of the key.
REMOVE <variable>
<indented list of expressions>
The variable must evaluate to a dictionary or a list. The
indented list of expressions is a list of expressions, one
to each line, that evaluate to lists. If the <variable> is
a dictionary, every key in the dictionary that matches any
value in any of the lists is removed from the dictionary.
If the variable is a list, every item in the variable list
that is the same as an item in a list generated by the list
of expressions is removed from the variable list.
SORT <optional expression>
<indented list of expressions representing lists>
Normally the sort is used with the optional expression not
present. The first list in the indented list of expressions
representing lists is sorted. Every other list in the list
is rearranged according to the sort of the first list. So
that if the first element of the first list is sorted into
the last position, then the first element of every list
is put in the last position. Another way of saying this
is that if each list represents a row, and the nth element
of each list represents the nth column, then SORT sorts the
columns in the lists according to the values in the first row.
If the optional expression is present it must evaluate to a
function of two variables that can be used to sort.
The <optional expression> may be provided as an anonymous
lam() function. See the discussion in the WebKeystone
Functions Chapter for a description of the lam() function.
In order to reverse the sort order, we can write:
SORT lam("x,y","cmp(y,x)")
or
SORT lam("x,y","-cmp(x,y)")
If we wish to sort only by the last three characters is a
string we might write:
SORT lam("x,y","cmp(x[:3],y[:3])")
The function must return a value of zero if the two
arguments are equal, a value of one if the first is
greater than the second, and a value of minus one if
the first is less than the second.
FILTER <optional expression>
<indented list of expressions representing lists>
This is used to shorten lists according to criteria given
by the optional expression. If the optional expression is
not present, then any element of the first list that evaluates
to zero, or the mull string, or the empty list, or any
other false value, will be deleted.
The corresponding elements in all of
the following lists will also be deleted. If the optional
expression is present, it must evaluate to a function. It will
be applied to each item in the first list. If the function
evaluates to false that item will be deleted and corresponding
items in all of the following lists will also be deleted.
FILTER lam("x","x[:1]=='a'")
Will filter out values whose first character is not an "a".
The values returned will be those which, when substituted
for the dummy variable in the expression following the
FILTER command, return a value of true.
ATTACH NOSUB myGifFile.gif
This command attaches the file myGifFile.gif to an email message,
without performing substitutions on the file.
RETURN RAW "Thank you!"
This command ends the process and sends the statement "Thank you!"
to the screen. You may include substitutions in the RAW string.
exist(<variable name>)
Returns 1 if the <variable name> is defined, 0 otherwise.
vPass(<password string>)
Returns 1 if the encrypted passwordVariable matches the
stored encrypted password. Returns zero otherwise.
subs(<any string>)
Performs substitutions on the string using the same
syntax as would be used for Endfiles.
swap() and who()
These functions are only available if you have a link
authorized. In addition they are only available from
the linked Profile. swap() swaps the UserID information
between the link and the invoking UserID. who() gives
the name of the current UserID.
tree(<Unique ID list><Parent list>[<Data Lists>]...)
tree(<List of Lists>)
This creates a tree object in WebKeystone. The first list,
the <Unique ID list>, identifies every unique node in the
tree with a label. This list is a list of unique labels.
The <Parent list> list identifies each node's parent by
label. Every label in the <Parent list> MUST exist in the
<:Unique ID list>. The lists must be the same length since
the position in the list also identifies the node. Although
it is possible to create circular references, it is not
possible to use them or print them. There can be any number
of data lists but each list must have one entry for each
Unique ID so that all of the lists are of the same length.
tree() returns a tree object which has several methods that
allow the tree to be manipulated. The tree object can also
be used in a replacement %(...)s substitution using templates
in order to effect a clean layout.
A tree is an object and has a number of methods, among which
are:
1. tree.parent(<node name>)
2. tree.exist(<node name>)
3. **********
It is also possible to take the length of a tree
(count the nodes) and to print a tree.
ind(<variable name>)
ind(<list of variable names>)
This allows the creation of a list of values given
a list of their variables. Many lists are provided
by WebKeystone, such as a list of the names
of all cgi-variables, a list of the names of all
required variables, etc. To access a list of the
corresponding values, use "ind":
ind(CGI_VARIABLES)
which will show the converted values of all cgi
variables.
oldv(<variable name>,<positive integer n>)
oldv(<list of variable names>,<positive integer n>)
This returns the nth previous value of <variable name>.
If the input value is a list, it returns a list of
all the previous variable values.
The current value is the 0th value. The previous
value is the 1st value. If the positive integer is
not given it defaults to 1. Here is an example:
oldv(emailAddress) == old(emailAddress, 1)
If a list of variable names is provided they are
all translated into a new list.
Old values are saved under the following conditions.
The oldvalue of a conversion or validation is saved.
The oldvalue of a variable rewritten during a FF_READ
or a DB_READ is saved. The old value of a variable is
saved if the "=>" mechanism is used.
limited(<variable name>)
Allows access to variables in the limited portion
of the UserID if the accessing UserID has the
LIMITED variable in the UserID set to 1 and is
either the owner of the UserID or has been accessed
through a link by the UserID. It is possible, and
even probable, that a User may not have access to
his own limited area. In order to access the variable,
its name must be known.
public(<variable name>)
Allows access to variables in the public portion of
the UserID by the owner of the UserID or by UserIDs
who have been accessed through the indirect link
mechanism. In order to access the variable, its name
must be known.
newid()
This creates a new unique id usable by the temporary
file mechanism. The result of this function is identical
to the NEWID command. The NEWID command is depreciated
in favor of this mechanism.
cleanHash(<value>,...)
cleanHash accepts a single value or a list of values
separated by commas. The value returned is based upon
the MD5 one-way encryption algorithm. Any change in any value
in the list will cause the returned value to be chaotically
different. That is, there is no known correlation between
the results of any two similar inputs. This is used to
encrypt and verify passwords and for other security
purposes.
lam("<dummy args>","<function>")
This provides an anonymous lambda-like function.
<:dummy args> is replaced by a list of dummy arguments
separated by commas and enclosed in quotes. <function>
is replaced by a function definition. The lam() function
can be assigned a variable and used repeatedly.
Example:
z = lam('x, y=7','x+y')
z(33) returns 40
z(7) returns 14
Since y is set in the argument list it can be overridden.
z(10,10) returns 20
f.nodup(<list>)
Returns a list with all duplicate
entries removed. The order of the list may be
changed by this function.
f.merge(<list1>, <list2>)
Combines two lists. The returned list
is <list1> with items from <list2> appended in
order to it. f.merge will not append an
item to the returned list that is already in the
returned list. This function does preserve order.
New items are added at the end of the returned list.
f.differ(<list1>,<list2>)
Returns <list1> with items removed
from it that are also in <list2>.
f.sum(<list>)
Adds the numeric values in <list>
together. It recursively enters all sublists.
String values that can be converted to numbers
are converted. String values that cannot be converted
are treated as zero. This function will convert
dollar amounts that begin with the "$" character.
f.prod(<list>)
Multiplies the numeric values in <list>
together. It recursively enters all sublists.
String values that can be converted to numbers
are converted. String values that cannot be converted
are treated as one. This function will convert
dollar amounts that begin with the "$" character.
f.add(<list1>,<list2>)
Adds the numeric values in <list1> to those
in <list2>. It converts strings to numbers. It duplicates
the shorter list until it is the same length as the longer.
Entries that cannot be converted to numbers are treated
as zeros.
f.sub(<list1>,<list2>)
Subtracts the numeric values in <list2> from
those in <list1>. It converts strings to numbers. It
duplicates the shorter list until it is the same length as
the longer. Entries that cannot be converted to numbers are
treated as zeros.
f.mul(<list1>,<list2>)
Multiplies the numeric values in <list1> to
those in <list2>. It converts strings to numbers. It
duplicates the shorter list until it is the same length as
the longer. Entries that cannot be converted to numbers are
treated as ones.
f.div(<list1>,<list2>)
Divides the numeric values in <list1> by those
in <list2>. It converts strings to numbers. It duplicates
the shorter list until it is the same length as the longer.
Entries that cannot be converted to numbers are treated
as ones.
f.mat(<list of lists>)
Before this operation is performed, shorter
lists are duplicated until every list has the same
length as the longest list in the <list of lists>. This
function makes a new list of lists in which the first list
is a list of the first elements in each list in the
<list of lists> and the nth list is a list of the nth
elements in each list of the <list of lists>.
f.compose(<list of lists>)
Similar to f.mat above except that before this operation
is performed, longer lists are truncated until every
list has the same length as the shortest list.
This function makes a new list of lists in which the
first list is a list of the first elements in each
list in the <list of lists> and the nth list is a list
of the nth elements in each list of the
<list of lists>.
f.full(<list>)
Returns true only if every element of
the <list> evaluates to a true value.
f.some(<list>)
Returns true only if at least one element
of the <list> evaluates to a true value.
f.ifThenElse(<expression>,<t-result>,<f-result>)
Returns <t-result> if <expression> is true and
<f-result> if <expression> is false.
f.fg_hash(<string>)
Creates an integer hashcode for the
object passed. This function is depreciated in favor
of cleanHash()
f.escape(<string>)
Replaces the characters (< > & % " and ') in a string
with the strings(< > & % $#34;
and ') respectively. These quoted HTML
equivalents prevent the characters from having
syntactical meaning to HTML and preserve the look of
the text in HTML. This function is very useful if
you intend to display information entered by the
user.
f.unEscape(<string>)
Reverses what the f.escape function does. This is
to return the string back to it's original form.
f.getEndline(<string>)
Macintosh, Windows, and UNIX computers all use different
'endlines' in their text files. Macintosh computers
use '\015', Windows computers use '\015\012', and UNIX
computers use '\012'. This function returns the characters
that are being used as an endline when given a text string.
f.removeTabs(<string>, <integer>)
Changes all tabs in a string to spaces. The second
argument defines how many spaces should be in the tab
stop.
f.wordWrap(<string>,<integer>)
Performs a reasonably intelligent word wrap on text in
in the string. The second argument is the number of columns
that should be in the returned string. This function is useful
when text is entered in a <TEXTAREA WRAP="OFF"> tag.
Variable names may include suffixes which cause implicit special processing
commands to be performed. The default delimiter for suffixes is the period; this may be changed using the wkSEPARATE command.
vEmail
Verifies that the email address has a value
preceding the "@" sign, and that the following
value contains at least one "."
vZip
Verifies that the value has 5 or 9 digits and
returns a value in the form #####-####
vInt
Verifies the value can be converted to an integer
and converts the value to an integer.
vFloat
Verifies that the value can be converted to a floating
point number and converts the value to a floating point
number.
vUSMoney
Verifies that the value (possibly containing a leading
"$" sign) can be converted to a monetary value and
converts the value to a string with a leading dollar
sign and two digits after the decimal point.
vCCNumber
Verifies that the value is a plausible credit card number,
checks with a redundancy algorithm, and returns the number
as #### #### #### ##### or #### ### ### ###
vDate
Accepts a date in any US format using all digits or
mixed digits and month names. Does not accept
illegal dates. Knows about leap years and centuries.
Returns it as mm/dd/yyyy
vCCDate
Accepts month and date without year.
vTime
Accepts a variety of time formats and returns them
as a 24 hour clock with the format hh:mm
vTel
Accepts a 10 digit telephone number and returns it as
(###) ###-####
vSSN
Accepts nine digit social security number and returns it
as ### ## ####
vURI
Validates a Universal Resource Identifier. This has been
built to exactly match the recommendation from W3C; the more restrictive URL
validations are not provided. However, these are easily emulated by checking the first part
of the URI for the appropriate sequence using regular expressions.
For instance, name="myurl.vURI.('<http://.*/>')" will validate for a
URI and then check for the particular URL that starts with the
"<http:///>" string. The value for "myurl" will be set if the
input is both a URI and starts with "<http:///>".
All validations and conversions are done left to right. The
result of the previous conversion/validation is passed to the
next conversion/validation.
.nN exactly n digits, if n=0, any number of digits
.nA exactly n alphas, if n=0, any number of alphas
.nX exactly n of any characters
.nZ no more than n of any characters
.(n) evaluates to a digit not greater than n
.(n,m) evaluates to a digit between n and m
.('<exp>') matches the regular expression (<exp>) between the
single (or double quotes)
+ combines two formats as in 0N+7Z (no more than 7 digits)
| ors two formats as in 7N|3A (seven numerics or three alphas)
Input fields can be validated using a regular expression. The syntax
is (' <exp> ') where <exp> represents a regular expression. The
regular expression is contained in single quotes and parenthesis.
For instance the field named "xxx" could validate to be three
lower case letters followed by one or more digits as follows:
<input ... name="xxx.('[a-z][a-z][a-z][0-9]+')
The regular expression language used is from Python. The Python
regular expression language is almost identical to that used by Perl.
A concise definition of the language is available at:
### Reference the Python Library here ###
It is also possible to trap variable using the named group feature
of regular expressions. The values in the named groups are passed
in a dictionary. This is an advanced usage of regular expressions.
You do not need named groups to simply use regular expressions to
validate.
If we wished to capture the first name of a person we could provide
two fields, or we could provide a single entry field and parse out
the first name with a regular expression such as:
<input name="name.('\s*(?P<firstName>\S+).*')">
The first name could then be accessed in a Profile by using
the "GroupDict" variable. As in:
first_name = GroupDict['name']['firstName']
Which would extract the group named 'firstName' from the validation
for name.
A more advanced example would be where we do both a conversion
and an extraction. If we wished to both validate for a telephone
number and trap the area code and number separately we could
write a field such as the following.
<input name="phone.vTel.('\((?P<areaCode>[0-9]{3,3}).*')">
and we could access the areacode with WebKeyscript code such
as:
acode = GroupDict['phone']['areaCode'])
If there are more than one item with the same name and groupname,
the value recovered will be in a list.
Parenthesis may be used for grouping. For example, (0N+7Z)|3A
would validate for zero to seven digits, or three alphas, if it were used as a suffix.
The registration feature is a security feature of
WebKeystone that prevents programs from probing your
http forms for weaknesses. Its purpose is to ensure
that, in any form accessing a particular profile,
the fields are all contained in their correct order
and that none are missing.
In order to register, one of
the fields in the form must contain the reg suffix.
If this is done, the checksum is calculated. If the
checksum matches a stored checksum, the REGISTRATION
variable is set. This variable may be checked by the
Profile. If it evaluates to 1, the checksum is
correct and the programmer should allow processing to
continue. If it evaluates to zero, then the http
request has been altered, or the form is coming from
an non-compliant browser, or the programmer has
altered the name of a field in the form and has not
reset the registration.
Registration information is kept in a file. The
default name of the file is 'Secure_Keys'. The name
of the file is configurable with the SECURITY_FILE =
'Secure_Keys' line in the wkConfig.py file by the
System Administrator. This file is stored in the top level
directory where Profiles are stored. If you wish to
reset all registration, simply delete the file and it
will be recreated automatically.
The first time the reg suffix is accessed, the
checksum is calculated and the REGISTRATION variable
is set. Each time after that the checksum is compared
with the one originally calculated. In order to have
a field NOT included in the checksum, use the opt
suffix. The opt suffix should be used on all check
boxes and radio buttons with no initial state. This
is because, if the box is unchecked, many browsers do
not include information on this field in the form
request. The opt suffix will handle this case.
<INPUT TYPE="HIDDEN" NAME="wkSEPARATE" VALUE="<delimiter>">
wkSEPARATE is used to change the default suffix delimiter to one
of your choice. The default is the period (.); it is changed to
what is specified in <delimiter>. For example, this command:
<INPUT TYPE="HIDDEN" NAME = "wkSEPARATE" VALUE = "#">
sets the delimiter to the pound sign (#) so you could have a variable
named like this:
Email#vEmail
dummy = debug(".*")
This command causes a list of all variables to be reported at the
end of the returned file. The variables you wish to have reported can also be included
in a list:
dummy = debug(["maximum","sales"])
This would report the values of the two variables "maximum" and "sales". Regular
expressions can also be included in the list. You may either enclose the names of variables in a list, or you may
provide a regular expression as a string. You may not put a regular expression in a list.
dummy = debug(".*","[A-Z].*")
This statement would cause all variables that do NOT start with A through Z to be
reported. debug will accept two parameters. The first is a list
or a regular expression that selects variables to be reported.
The second argument, if present, is a list of variables to be
excluded. If a variable in the second list is selected by
the first argument to be included, it is removed if it is
selected by the second list. The second argument may be either
a list or a regular expression.
<INPUT TYPE="HIDDEN" NAME="TPersist_" VALUE="AAAn79xqRttM0_1999">
The values that this recovers must have been created with
the TF_WRITE command. They are recovered
to the name space of the Profile. The Profile and
the UserID may also be hidden using this technique.
This is an advanced technique. It has the advantage
that it shortens the number of hidden fields and hides
their contents.
Updated 2001-4-18