MIVA Corporation: MIVA Merchant 4.x User Guides
MIVA, Infinite Growth...  Tools To Help Your Business Grow
Log in screen
spacer gif

Home // Docs // Miva Script Reference

Miva Script Reference Manual

Document Revision 1.4
For Miva Script executed by Miva Empresa v3.76 and Miva Mia v3.76

Table of contents

Tags

<MIVA>
<MvADD>
<MvASSIGN>
<MvCALL>
<MvCALLSTOP>
<MvCLOSE>
<MvCLOSEVIEW>
<MvCOMMENT>
<MvCOMMERCE>
<MvCOMMERCESTOP>
<MvCREATE>
<MvDELETE>
<MvDO>
<MvELSE>
<MvEVAL>
<MvEXIT>
<MvEXPORT>
<MvFILTER>
<MvFIND>
<MvFUNCRETURN>
<MvFUNCTION>
<MvGO>
<MvHIDE>
<MvIF>
<MvIMPORT>
<MvIMPORTSTOP>
<MvLOCALIZED>
<MvLOCKFILE>
<MvMAKEINDEX>
<MvOPEN>
<MvOPENVIEW>
<MvPACK>
<MvPOP>
<MvPOPDELETE>
<MvPOPSTOP>
<MvPRIMARY>
<MvQUERY>
<MvREINDEX>
<MvREVEALSTRUCTURE>
<MvSETINDEX>
<MvSKIP>
<MvSMTP>
<MvUNDELETE>
<MvUPDATE>
<MvWHILE>
<MvWHILESTOP>


Find out about free Miva Script technical support services on our web site.

Miva Script programs

Miva Script programs are HTML documents that also contain tags from the Miva Script programming language. Miva Script is a 'server-side' scripting language that is implemented by Miva Empresa and Miva Mia rather than by the browser (by contrast, a 'client-side' language such as JavaScript is implemented by the browser). Miva Script programs can also be called scripts, active documents, or simply documents. Miva Script tags correspond to typical programming language constructs such as assignment statements, conditional expressions, loops, and input/output statements, as well as Miva Script's special database, mail, and commerce functionality. Miva Script tags are XML-based: they have the same format as HTML tags; the element names (for example, <MvASSIGN>) indicate their function; and the attributes specify values that the tags operate on. Miva Script tags can be freely mixed with HTML tags. We speak of Miva Script programs being interpreted (by the Miva Empresa or Miva Mia pre-processor) and of individual tags being executed.

Miva Script programs use the following kinds of auxiliary files:

  • Delimited (Flat) Data files - Text files that contain one record per line, with fields separated by one or more ASCII characters. The last line should be blank.
  • Miva Database Files - Databases that can be ordered by multiple index files. A database may have a memo file (.dbt file) associated with it.
  • Miva Index Files - Files that create an ordered view of the database files.

When a browser requests a Miva Script document, Miva Empresa or Miva Mia will pre-process the document before passing it on to the browser:

  1. The Miva Script tags (commands) are executed, and replaced by the results of the execution, which is generally a mixture of HTML tags and text (there may also be input to and output from external files).
  2. The pre-processed document, consisting of the original HTML tags and text, together with the tags and text generated by Miva Empresa or Miva Mia, is served to the browser and displayed.


Creating URLs for Miva Script programs

Miva Script programs should have a distinctive file extension. Unless some other extension has expressly been established on your system, we recommend the .mv extension.

The URL that you use to access your documents depends on whether you are using Miva Empresa or Miva Mia, and in the case of Miva Empresa, how the server is configured.



Miva Empresa

If Miva Empresa is running as a CGI application, it will be installed in the cgi-bin directory on your server. Typically Miva Empresa can be accessed by a URL in the following format:

http://www.your_isp.com/cgi-bin/miva

If in doubt, you should confirm this location with your ISP.

If your ISP is providing you with a virtual domain, Miva Empresa can be configured to run with a URL of the following format:

http://www.your_domain.com/cgi-bin/miva

When Miva Empresa is installed and configured, a specific directory is designated as the Miva Script document directory (with the mivaroot parameter in the Miva Empresa global configuration file). This is the default location for running Miva Script programs on your server. To run programs located in that directory, you would use a URL of the following form:

http://www.your_isp.com/cgi-bin/miva?scriptname.mv

That is, put a '?' between the Miva Empresa URL and the script name. If you are running a virtual domain, use the other form of the Miva Empresa URL:

http://www.your_domain.com/cgi-bin/miva?scriptname.mv

If authorized, individual users can also run scripts from their home directories. Miva Empresa designates a subdirectory of the user's home directory as their script directory. By default this is the directory public_html, but Miva Empresa can be configured to use another directory by way of the userdir parameter in the Miva Empresa global configuration file. To run a script called apples.mv in his or her public_html directory, the user jsmith would use the following URL:

http://www.your_isp.com/cgi-bin/miva?~jsmith/apples.mv

That is, the '?' should be followed by ~username, followed by the script name.

The NSAPI (Netscape API) version of Miva Empresa does not require that a Miva Script document be in a particular location; instead, the file name extension .mv triggers Miva Empresa. For example:

http://www.your_isp.com/apples.mv


Miva Mia

If you are running Miva Mia, there is only one Miva Script document folder, also called the HTML document folder, whose location is indicated in the WWW tab of the Miva Mia properties dialog. The URL for accessing the Miva Mia server is indicated in the Status tab. Typically, a script in your documents folder can be accessed with a URL in one of the following forms:

http://host.domain/script.mv
http://your_IP_address/script.mv
http://localhost/script.mv
http://127.0.0.1/script.mv

See the Miva Mia Administration page for more information.



Data files

Data files (external input and output files, database files, database indexes) used by Miva Script programs must be stored in specific locations.

For Miva Empresa these locations are set for specific users or groups in the Miva Empresa authorization file. By default, a user's data directory is the directory mivadata in their home directory. Contact your ISP or system administrator to find out the configuration for your server.

For Miva Mia the data folder is indicated in the WWW tab of the Miva Mia properties dialog.

There is no limit on the length of a filename that can be used by Miva Script, except for any limit that may be imposed by the operating system.



Writing a Miva Script program

This section describes a small Miva Script program to get you started. Even if you don't understand everything, you may find it useful to enter the program and open it with your browser, just to get an idea of what Miva Script programming is like and how to run a program.

<HTML>
<HEAD>
<TITLE>A simple Miva Script program</TITLE>
</HEAD>
<BODY>
<H2>A simple Miva Script program</H2>
<MvASSIGN NAME="var1" value="5">
<MvASSIGN NAME="var2" value="8">
<P><b>The answer is: <MvEVAL EXPR="{var1 + var2}">.</B></P>
</BODY>
</HTML>

If you save this document in the Miva Script document folder and submit it to your browser, Miva Empresa or Miva Mia will process it and send the results to the browser. The browser should display something like this:



A simple Miva Script program

The answer is: 13.

Even though this program is simple, it illustrates several important features of Miva Script programs:

  • Miva Script tags can be mixed freely with regular HTML tags.
  • Variables: the two <MvASSIGN> tags assign values to variables.
  • Expressions: {var1 + var2} is an expression that adds together the values of two variables.
  • Evaluation: the <MvEVAL> tag evaluates the EXPR (expression) and displays the result.

These features are explained further in the sections that follow.



Tags

All Miva Script tags (also called elements) consist of a start-tag, such as <MvIF>, and sometimes an end tag, such as </MvIF>, that begins with the '</' characters.

End-tags are either required or prohibited: tags that can have end-tags must have them in order for the program to be correct; for all other tags, end-tags are prohibited. Tags for which end-tags are prohibited are called empty tags. Not only do empty elements not have end-tags, you cannot enter any 'content' for these elements, except for attribute values in the start-tag.

The <MvIF> tag requires an end-tag:

<MvIF EXPR="{age GT 6}">
...some code and/or text...
</MvIF>

<MvEVAL> is an empty tag that has attributes:

<MvEVAL EXPR="{age + 1}">

<MvELSE> is an empty tag that has no attributes:

<MvELSE>

The summary sections in this manual for each tag will indicate an end-tag if one is required.

If an element with paired tags is nested inside another element, the tags must be balanced; that is, the innermost element's start- and end-tags must both be between the start- and end-tags of the outermost element. For example:

<MvCOMMENT> Incorrect! </MvCOMMENT>
<MvWHILE ...>
    <MvIF ...>
...
</MvWHILE>
    </MvIF>

<MvCOMMENT> Correct! </MvCOMMENT> 
<MvWHILE ....>
    <MvIF...>
...
    </MvIF>
</MvWHILE>

If you use a context-sensitive editor such as HoTMetaL PRO to create Miva Script code, this kind of problem will not happen.



Attributes

Most start-tags can contain attributes: these are names that are assigned a value surrounded by double-quotes. Attribute values are data or other instructions that are used by a Miva Script or HTML tag. For example:

<MvASSIGN NAME="age" VALUE="10">

This tag has two attributes, NAME and VALUE, with values age and 10, respectively. The <MvASSIGN> tag assigns the value (10) specified with VALUE to the variable (age) specified with NAME.

Attribute values must be surrounded by double quotes for SGML and XML conformance.

Attribute values are often specified as literal values (text or numbers), as in the example above. Any attribute value can be specified as an expression or macro, however. Before the tag containing the attribute is executed, the expression or macro will be evaluated and the resulting values assigned to the attribute. For example:

<MvASSIGN NAME="person1" VALUE="shirley">
<MvASSIGN NAME="&[person1];" VALUE="{5+10}">

In the second <MvASSIGN>, the macro &[person1]; evaluates to 'shirley', and {5+10} evaluates to 15, so this tag is equivalent to:

<MvASSIGN NAME="shirley" VALUE="15">

In the tag summary sections of this manual, the expected values given for attributes indicate the values' semantics, that is, what kind of value--filename, mail server, function name, URL, etc. is required. In some cases, the value of an attribute must be one or more variables; otherwise, a literal, expression, or macro can be used to provide the attribute value. (A macro that evaluates to a variable name can also be used in place of a variable name.)

Using tags inside tags

Miva does not interpret tags used inside other tags as attribute values. For example, the following tag will not have the (intended) effect of specifying a URL:

<A HREF='<MvEVAL EXPR="{guestbook_url}">'>Say hi</A>

Sometimes you can accomplish what you want to do by using a macro. You could rewrite the previous example as follows:

<A HREF="&[guestbook_url];">Say hi</A>

In general you you cannot use a Miva Script tag as the value of the attribute of an HTML tag, but you can use an HTML tag as the value of the attribute of a Miva Script tag. For example:

<MvEVAL EXPR="<INPUT TYPE='text' NAME='speak'>">

Another way to express this is:

<MvASSIGN NAME="textbox" VALUE="<INPUT TYPE='text' NAME='speak'>">
&[textbox];

These examples will display a text box in the browser.

You can use the same constructions to run other HTML code, such as applets.

If you embed a tag containing attributes in an attribute, you must use a different quote character to delimit the attributes of the embedded tag than is used to delimit the attribute in which the tag is embedded. See Using quotes and other special characters in expressions for more information.



Putting comments in your program - MvCOMMENT

A comment is a piece of code that is not executed or displayed, but is present to indicate what the program is for and what individual pieces of code do. Comments are invaluable to someone unfamiliar with the program who is trying to understand it, or even to the program's author, who may be reading it again several weeks or months after writing it. Comments are also useful when you are developing, debugging , and testing your program: you can surround with comments lines of code that you think may have problems, to make sure they do not get processed, and then observe the results when the program is run.

Comments start with <MvCOMMENT> and end with </MvCOMMENT> :

<MvCOMMENT> this is a comment </MvCOMMENT>
<MvCOMMENT> 
This is a comment too, 
because comments can 
span multiple lines. 
</MvCOMMENT>

Text or code placed between <MvCOMMENT> and </MvCOMMENT> tags will not be passed on to the Miva Engine or the browser--it is completely ignored. .

You can also use ordinary HTML comments; these are passed to the browser but are not displayed. For example:

<!-- I am a comment. -->


Miva Script variables

A variable is a name that is associated with a value. For example, the name x could have the value 10, or, since it's helpful to use names that indicate what they're used for, age could have the value 10.

In Miva Script, a variable name can consist of the letters a through z (upper and lower case), the underscore, '_', and the digits 0 through 9. Other characters are permitted if they are 'escaped' with a backslash. For example:

<MvEVAL EXPR="{age\-old}">

This evaluates the variable age-old; without the backslash, this expression would be interpreted as the value of age minus the value of old. The backslash is not required if the variable is used in a macro.

The period (dot), '.', character can appear in variable names, but only when following a prefix that indicates the scope in which the variable is understood.

If the variable will be used in an expression, the variable name must start with a letter or an underscore. Variables that start with numbers are not supported in expressions unless they start with a scope prefix (g., l., s., or d.). Variables used in macros are permitted to start with a number.

It is recommended that you avoid using variable names that start with numbers or contain 'escaped' characters, unless they are required because they correspond to the names of fields in an HTML form, and no alternative names can be used.

Miva Script variable names are not case-sensitive: the variable age is the same as AGE and Age.

Here are some examples of valid and invalid usage:

<MvCOMMENT>Valid</MvCOMMENT>
<MvEVAL EXPR="{_13user}">
<MvEVAL EXPR="{user_13}">
<MvEVAL EXPR="{g.user_13}>
<MvEVAL EXPR="{g.13_user}">
&[13_user];
&[g.13_user];

<MvCOMMENT>Invalid</MvCOMMENT>
<MvEVAL EXPR="{13_user}">
<MvEVAL EXPR="{g.user.13}">

Unlike in some programming languages, you do not need to declare or define variables--you can just go ahead and use them. Using a variable in an expression without first assigning it a value also does not cause an error (although it may, of course, be responsible for a logic error in your program). Miva Script variables are typeless: the same variable can be assigned a numerical, text string, or boolean (true/false) value.

You can give a variable a value in two ways:

  • Assigning a variable name with a value using the Miva Script <MvASSIGN> tag.
  • Assigning a variable name by way of a standard input field from an HTML form.

To display the value of a variable, use the <MvEVAL> tag or a macro:

<MvEVAL EXPR="{var}">
&[var];


Using <MvASSIGN>


Summary

<MvASSIGN 
   NAME="var"
   VALUE="{expression}|literal">

<MvASSIGN> assigns the variable var the value given by expression or literal. <MvASSIGN> is an empty tag.


The simplest way to assign a variable a value is with the <MvASSIGN> tag. For example:

<MvASSIGN NAME="age" VALUE="10">

This gives the variable age the value 10.

<MvASSIGN> has two attributes, NAME and VALUE, which specify the variable name and value, respectively.

Both the name and value can be specific values, such as age and 10 in the previous example, or they can be expressions, which are evaluated only when the program is run. For example:

<MvASSIGN NAME="age" VALUE="{2 + 8}">
<MvASSIGN NAME="age" VALUE="{age + 2}">
<MvASSIGN NAME="age" VALUE="{thisyear - birthyear}">

These examples show three different kinds of expressions (the expressions start with '{' and end with '}'). The first expression consists of two numbers added together. The second expression adds 2 to whatever the current value of age is (this value then becomes the new value of age). The third expression uses two other variables (birthyear and thisyear) and subtracts them to calculate the new value of age.

Although these examples use expressions only in the value of the VALUE attribute, the NAME attribute can also have an expression as its value.


Note: <MvASSIGN> should be used instead of the deprecated tag <MvLET>. Miva Script supports this tag to maintain compatibility with older scripts, but it should not be used for new coding. <MvLET> is not XML-compliant and will cause problems if the code is validated or edited with an XML-compliant editor. <MvLET> also cannot use an expression to represent the name of the variable being assigned (by contrast, <MvASSIGN>'s NAME attribute can have an expression as its value).



Assigning variables in HTML forms

A variable can be created and assigned a value in an HTML form. Since HTML forms are the primary way of obtaining input from users, you will often be assigning values to variables using this method.

In an HTML form, each button, text box, pull-down menu, and scrolling list has a name attribute. For each such object, Miva Script creates a global variable whose name is the same as the value of the object's NAME attribute. When the form is submitted, the values entered for each object in the form are assigned to the corresponding variables. For example, the following markup creates a text box with the name speak:

<INPUT TYPE="TEXT" NAME="speak">

Based on this, Miva Script will create a variable called speak; it will be assigned a value if a user enters a value in the text box and submits the form to the Miva Script program, and can then be manipulated and operated upon by the receiving program.

For text boxes and text areas, the value of the variable is whatever text the user enters. Check boxes, radio buttons, scrolling lists, and pull-down menus are defined with a fixed set of one or more possible values, specified by the object's VALUE attribute. The Miva Script variables corresponding to these form objects can have one of these fixed values when the form is submitted, but can be given other, arbitrary values later in the Miva Script code. For example:

<INPUT TYPE="CHECKBOX" NAME="send_info" VALUE="yes">

If this checkbox is turned on when the form is submitted, the variable send_info will have the value yes.

Here is another example, using radio buttons:

<B>Language/langue:</B><BR>
<INPUT TYPE="RADIO" NAME="language" VALUE="english">English<BR>
<INPUT TYPE="RADIO" NAME="language" VALUE="french">Français<BR>

In this example, the variable language can have the value english or french (depending on which button is selected) when the form is submitted.


Note: Variables assigned in HTML forms are not available to a program until the form has been submitted.


Note: An 'image submit' button (<INPUT TYPE="IMAGE"> called name is represented by two Miva Script variables: name.x and name.y will contain the X and Y coordinates of the point that was clicked on when the form was submitted.


Note: If a drop-down list (SELECT tag) has more than one option selected, the value of the variable corresponding to the list will be the value of the last selected option in the list.

See the section Multiple passes through a program for more information on using variables set in forms.



Variable scope

The scope of a variable is the context in which it exists and has meaning. There are two aspects of variable scope: the a variable's scope inside the currently running program, and its scope in another program called by the current program.

By default, the maximum scope of any variable is the currently running program instance; if this program calls another program (or calls the same program a second time), the values of variables in the current program are not automatically available in the second program. There are several methods for passing variables and other data to a program: see Passing data to a program below.

Prefixes in the variable name define its scope in the current program. The variable scopes available in Miva Script are:

  • Local: if a variable is created with the prefix local or l (for example, local.age and l.age), and is used inside a function (an <MvFUNCTION>...</MvFUNCTION> block), its scope is only inside that function.
  • Database: the scope a variable whose name is of the form dbname.database.var or dbname.d.var is the database named dbname and commands that act on that database.
  • System: Whenever a built-in system variable or a variable that is generated by an <MvSMTP>, <MvPOP>, <MvWHILE>, or <MvCALL> loop is named, use the prefix convention system.varname or s.varname.
  • Global: if a variable is created with the prefix global or g (for example, global.age and g.age), it is a global variable--its scope is the entire program.

A global variable and a local variable with the same primary name can be used as two different variables if they're prepended with the appropriate prefixes (for example, g.counter and l.counter).

If a variable var without a prefix is encountered, the Miva pre-processor carries out the following steps in order until one of them succeeds:

  1. The Miva pre-processor checks if a system variable s.var or system.var exists and has scope in the current location. If so, then var is interpreted as referring to that variable.
  2. If a local variable l.var or local.var exists and has scope in the current location, then var is interpreted as referring to that variable.
  3. If a database variable d.var or database.var exists and has scope in the current location, then var is interpreted as referring to that variable.
  4. If a global variable g.var, global.var, or var exists then var is interpreted as referring to that variable.
  5. If no variable var (with any prefix) exists, then var is created as a global variable.

The built-in function miva_getvarlist(scope) returns a comma-separated list of the names of all currently defined variables with the given scope. scope must be a literal string: 'l', 'local', 'g', 'global', 's', 'system'.


Note: For xBase3 databases, use <MvREVEALSTRUCTURE>

The following detailed example illustrates these rules (for simplicity, only global and local variables are contrasted here).

<BODY>
<MvFUNCTION NAME="f1">
E: <MvEVAL EXPR="{new1}"><BR>
F: <MvASSIGN NAME="new1" VALUE="20"><BR>
G: <MvEVAL EXPR="{g.new1}"><BR>
H: <MvASSIGN NAME="l.new1" VALUE="30"><BR>
I: <MvEVAL EXPR="{new1}"><BR>
J: <MvEVAL EXPR="{g.new1}"><BR>
K: <MvASSIGN NAME="new1" VALUE="40"><BR>
L: <MvEVAL EXPR="{l.new1}"><BR>
M: <MvEVAL EXPR="{g.new1}"><BR>
</MvFUNCTION>
A: <MvASSIGN NAME="new1" VALUE="10"><BR>
B: <MvEVAL EXPR="{new1}"><BR>
C: <MvEVAL EXPR="{g.new1}"><BR>
D: <MvASSIGN NAME="junk" VALUE="{f1()}"><BR>
N: <MvEVAL EXPR="{new1}"><BR>
...
</BODY>

The output from this program is:

A: 
B: 10
C: 10
D: 
E: 10
F: 
G: 20
H: 
I: 30
J: 20
K: 
L: 40
M: 20
N: 20

It is worth analyzing this output in detail:

  1. Assign new1 the value 10.
  2. As expected, evaluating new1 displays '10'.
  3. Since new1 was used for the first time without a prefix, it is a global variable and therefore g.new1 also evaluates to '10'.
  4. Call the function f1().
  5. Since new1 is global, it is available inside the function block and (currently) has the same value, 10.
  6. Assign a new value, 20, to new1.
  7. Evaluating g.new1 gives '20', indicating that the previous assignment to new1 still referred to the global variable.
  8. Create a local variable l.new1 and assign it the value 30.
  9. Evaluating new1 now displays '30'; since a local new1 now exists, new1 without a prefix now refers to that version of the variable.
  10. The global variable new1 is still available, but you have to use the g. prefix to access it.
  11. Assign the value 40 to new1.
  12. Evaluating l.new1 displays '40', indicating that the previous assignment referred to the local version.
  13. g.new1 (the global variable) still has the value '20'
  14. Now the function has terminated; since the local version of new1 has no scope here, evaluating new1 (without a prefix) now refers to the global variable new1 again. It still has the last value assigned to the global variable, on line F.

Note: Variables used in documents in a frameset document are local to the frame in which they are used.



Site variables

System administrators of servers running Miva Empresa can maintain a file called the site variables file that contains definitions for variables (but not functions) that are accessible to all Miva Script programs running on the server. The Miva Mia users can maintain site variables files on their PCs. A variable in the site variables file can be used in the same way as any other global variable. Variables in a the site variables file are intended to be assigned string values only; the results of assigning Miva tags to variables are undefined.



Expressions and literals

An expression is a formula that can be evaluated to give a result. Expressions always occur in attribute values and must be enclosed in curly brackets, '{' and '}'. Expressions can contain variables, literal values (text or numbers), and operators, which indicate how the other components in the expression are to be evaluated.

In general, Miva Script expressions can be used in two ways:

  1. They can create a new value.
  2. They can be evaluated for their logical "truthfulness". Any expression that is equal to '0' (zero) or a null string is considered false. All others are considered true. True is often represented by '1' (one).

A literal is a number or a string of characters that is not meant to be evaluated: for example: 23 and 'hi there'. Literals don't have to be enclosed in curly brackets.

In general, if an attribute value (except for a macro) needs to be evaluated, it must be enclosed in curly brackets.

When an value surrounded by curly brackets is used in an attribute value, the open bracket must immediately follow the double-quote character that starts the attribute value (that is, there must be no spaces between them; similarly, the closed bracket must immediately precede the double-quote that terminates the attribute value. If you don't do this, the expression will be treated like a literal string. For example:

<MvCOMMENT> Wrong! </MvCOMMENT>
<MvEVAL EXPR=" {a + b} ">

<MvCOMMENT> Right! </MvCOMMENT>
<MvEVAL EXPR="{a + b}">

Here are some examples of expressions:

{age}
{age + 1}
{age GE 17}
{x * (y / z)}
{'a' IN name}

These examples contain:

  • Variables (age, name, x, y, z)
  • Numbers (1, 17)
  • Literal text ('a')
  • Arithmetical operators (+, *, /)
  • A numerical comparison operator (GE--"greater than or equal to")
  • A string (text) comparison operator (IN--"is contained in")

Notice also that a single variable name can be an expression. Conversely, an expression can have as many components as are required.

Inside an expression, spaces are not significant (that is, they do not affect how the expression is evaluated) except in the following ways:

  • If an operator contains characters that can also occur in a variable name, then it must be separated from the other components by one or more spaces: {n POW 2} cannot be written as {nPOW2}, but {a + b} and {a+b} are equivalent (since '+' cannot be a character in a variable name).
  • Spaces inside literal strings are significant.

One use for expressions that you've seen already is in a variable assignment:

<MvASSIGN NAME="age" VALUE="{age + 10}">

Expressions are also used in conditionals, which test some condition and (usually) take an action based on the results of the test. Here is an example:

<MvIF EXPR="{age GE 17}">
    <p><b>This person is eligible to drive a motor vehicle.</b></p>
</MvIF>

This <MvIF> tag tests the expression '{age GE 17}' ("Is the age greater than or equal to 17?") and if that expression is true (which depends on the current value of age), it prints some text.

An expression consisting only of a literal string does not require curly brackets. If a literal string is inside curly brackets, it must be surrounded by single quotes (that is, the apostrophe or front quote character, '...'). The following tags are equivalent:

<MvASSIGN NAME="person" VALUE="Moira">
<MvASSIGN NAME="person" VALUE="{'Moira'}"> 

If you want to put a literal string of text in an expression that contains operators or variables, surround the literal with single quotes . For example:

<MvASSIGN NAME="boss" VALUE="{'Fred ' $ 'Flintstone')">

Here the literals Fred and Flintstone are surrounded by double quotes.

You should represent a null string in an expression as two single quotes (''):

<MvIF EXPR="{entry EQ ''}">

The Miva Script comparison or logical operators (such as GE and AND) always produce a result of 0 (false) or 1 (true).


Note: An expression consisting of a literal number, for example, {149.99}, evaluates to that number.



Using quotes and other special characters in expressions

Since expressions have to be surrounded by double quotes, you have to use special techniques if you want to put double quotes in expressions.

There are two ways to handle this:

  1. Use single quotes instead
  2. Use the built-in function asciichar() to represent the double quotes

If you want to assign a string of HTML (but not Miva Script) code to a variable, you can use single quotes instead of double quotes to surround attribute values. The code in the variable can then be passed to the browser using <MvEVAL>. Since the browser cannot execute Miva Script code, you should pass only HTML code to the browser in this way.

<MvASSIGN
     NAME="input1" 
     VALUE = "Address: <INPUT TYPE='text' NAME='address'>"> 
<FORM 
    ACTION = "http://www.sq.com/cgi-bin/quagmire" 
    METHOD = "post">
<MvEVAL EXPR = "{input1}">
</FORM>

This displays:

Address:

The code in this example assigns a string to a variable named input1. The value of input1 is HTML code that generates a text box with the label "Address:". Miva will evaluate input1 to send the HTML code to the browser; it also creates a Miva Script global variable called address (or g.address).

If you really have to use double quotes, you can use the built-in function asciichar(n) to represent the double quotes. asciichar(n) is equivalent to the character whose ASCII code is n. The code for a double quote happens to be 34. You will also need to use the concatenation operator, $, to join this character to the rest of your text. For example:

<MvASSIGN 
    NAME="banner" 
    VALUE="'Home of '$asciichar(34)'The Best 
            Wings in Town'$asciichar(34)'!'">
<MvEVAL EXPR="{banner}">

This displays:

Home of "The Best Wings in Town"!

Notice that the literal parts of the expression have to be surrounded by single quotes.

Other special characters

You can use the asciichar(n) function to represent other characters that you cannot use literally in an expression, for example, carriage return and line feed. If you will be using these characters often you can assign asciichar(n) to a variable and use the variable instead. For example:

<MvASSIGN NAME="dq" VALUE="{asciichar(34)}">
<MvASSIGN NAME="cr" VALUE="{asciichar(13)}">
<MvASSIGN NAME="lf" VALUE="{asciichar(10)}">
<MvASSIGN NAME="crlf" VALUE="{asciichar(13)$asciichar(10)}">
<MvEVAL EXPR="{'Dear User,'$crlf$'Thank you for your interest.'}">


Displaying the result of an expression - MvEVAL


Summary

<MvEVAL EXPR="{expression}">

<MvEVAL> evaluates and displays the value of expression. <MvEVAL> is an empty tag.


There are many ways of evaluating an expression, but if you want to display the result in the browser, you should use the <MvEVAL> tag. For example:

<p><b>The final amount is: <MvEVAL EXPR="{price*units}">.</b><p>

Another way to think about the <MvEVAL> tag is that it is replaced, in an HTML document, with the result of an expression.

The <MvEVAL> tag has one attribute, EXPR, whose content can be an expression to be evaluated, or a literal string or number. If the content is an expression it must be enclosed in curly brackets, or it will be treated like a literal and not evaluated. Normally, if you want to display a literal you would just enter it directly in the HTML file, rather than using an <MvEVAL>.

When multiple fields in a form are given the same name (e.g. "name"), return values will be comma delimited. For example, the following:

	name1<INPUT TYPE="text" NAME="name"><BR>
	name2<INPUT TYPE="text" NAME="name"><BR>
	name3<INPUT TYPE="text" NAME="name"><BR>
	name4<INPUT TYPE="text" NAME="name">
	<INPUT TYPE="submit VALUE="Submit Query">
name1
name2
name3
name4

The results of "<MvEVAL EXPR="{ name }">"

...will be "1,2,,4"

See also the section on macros.



Miva Script operators

An operator in a Miva Script expression indicates how the data it acts on are to be processed: added, subtracted, compared, joined, bit-shifted, and so forth. In the expressions {age + 10} and {age GT 16}, '+' and 'GT' are operators. The components on each side of the operator are sometimes called operands.

Miva Script operators have a built-in precedence that determines which operator gets interpreted first if there are two or more operators in an expression. You can also override the built-in precedence by surrounding sub-expressions with parentheses, `(' and ')'.

Miva Script provides the following types of operators:



Arithmetical operators

These operators are used with numbers or numeric expressions.

Miva Script also supports a number of built-in numerical functions.

+
Addition
expr1 + expr2
-
Subtraction
expr1 - expr2
*
Multiplication
expr1 * expr2
/
Division
expr1 / expr2
POW
POWer
expr1 POW expr2
Raise expr1 to the power expr2
For example, 3 POW 4 is 34, or 81.
MOD
MODulus
expr1 MOD expr2
Returns the integer remainder from expr1/expr2
For example, 26 MOD 7 is 5. See also the built-in function fmod(), which returns floating point remainders.
ROUND
Number rounding
number ROUND places
Rounds number up or down to places decimal places.
For example, 123.45676 ROUND 2 is 123.46

Note: A minus sign can precede a literal number to make it negative (for example, -3.14), but otherwise must be used strictly as a binary operator: {-x} evaluates to the literal string '-x'; to express the negative of a value, use {-1*x} or {0-x}. Using +, /, or * as a unary operator results in an expression error.



Comparison operators

These operators are used to compare two numbers or two text strings. In text string comparisons, lowercase letters are considered to be greater than uppercase letters. Two strings are equal only if the case (upper or lower) matches letter-by-letter. These operators all give a value of 1 (true) or 0 (false).

GT
Greater Than
expr_a GT expr_b
Tests whether expr_a is greater than expr_b
LT
Less Than
expr_a LT expr_b
Tests whether expr_a is less than expr_b
EQ
EQual To
expr_a EQ expr_b
Tests whether expr_a is equal to expr_b
Two strings are equal only if the case (upper or lower) matches letter-by-letter.
NE
Not Equal To
expr_a NE expr_b
Tests whether expr_a is not equal to expr_b
GE
Greater Than or Equal To
expr_a GE expr_b
Tests whether expr_a is greater than or equal to expr_b
LE
Less Than or Equal To
expr_a LE expr_b
Tests whether expr_a is less than or equal to expr_b

Note: The expression {0 EQ ''} ("does zero equal the null string?") returns 1 (true).


Tip: To check whether a string is non-null, you can use the expression {string} rather than the longer form {string NE ''}. Similarly, to test whether a number is non-zero, use {number} rather than {number NE 0}.


Tip: Strings that consist of numbers and punctuation characters, and begin with identical numeric prefixes (for example '234-1' and '234-2') are considered equal by EQ. To force Miva Script to perform a true string comparison, prepend the same letter to both sides of the EQ. For example:
{'x' $ lhs EQ 'x' $ rhs}



Logical operators

These operators are used with 'true' or 'false' expressions. For example:

<MvIF EXPR="{age GE 17 AND age LT 80}">

This expression is true if both of the expressions '{age GE 17}' and '{age LT 80}' are true.

NOT
Logical NOT
NOT expr
Returns the opposite of expr (if expr is false, NOT expr is true, and vice versa). Notice that this operator is unary: it acts on one expression, not two.
AND
Logical AND
expr_a AND expr_b
This expression is true if both expr_a and expr_b are true.
OR
Logical OR
expr_a OR expr_b
This expression is true if either of expr_a and expr_b is true.


Text string operators

These operators are used with text strings or text string expressions.

Miva Script also supports a number of built-in string functions.

$
Concatenate strings
expr_a $ expr_b
Concatenates (joins) the strings expr_a and expr_b together.
For example, {'fred' $ 'flintstone'} would result in 'fredflintstone'.
IN / CIN
Beginning position
expr_a IN expr_b
expr_a CIN expr_b
Returns the beginning position of expr_a contained in expr_b.
For example, {'da' IN 'canada'} returns 5, because 'da' begins at the fifth letter in 'canada'. IN is case-sensitive: it will find matches only if the matched sub-string in expr_b has the same case, letter-by-letter, as expr_a. Use the CIN operator if you want to make your comparison case-insensitive. Notice that if you use literal strings in this expression you have to surround them with single quotes.
EIN / ECIN
End position
expr_a EIN expr_b
expr_a ECIN expr_b
Returns the end position of expr_a contained in expr_b.
For example, {'dia' IN 'canadian'} returns 7, because 'dia' ends at the seventh letter in 'canadian'. EIN/ECIN returns 0 when the left operand is NULL. This fixes backward comptibility with Miva v3.57 and earlier "Htmlscript" versions. ECIN is the case-insensitive version of EIN.
SUBSTR
Substring
expr SUBSTR 'position, #chars'
Returns the substring of expr that begins at position and is #chars characters in length. For example, {'canadian' SUBSTR '3,5'} returns 'nadia', which starts at the third character in 'canadian', and is five characters long. Note that the right side of a SUBSTR expression must be a string consisting of two numbers separated by a comma, or an expression or macro that evaluates to such a string.
FMT
Format string
expr FMT pattern

The FMT operator enables you to format a string based on a pattern. This operator provides a way to enhance the search, display, and logical processing capabilities of Miva Script active documents. New users are advised to gain some experience with constructing expressions using the built-in string functions before trying FMT. See the section Formatting values for a discussion of FMT string format patterns and modifiers.

CRYPT
Encrypt a string
plaintext CRYPT key

Performs a one-way encryption, similar to that provided with the UNIX crypt command. The string plaintext is encrypted using the string key (this string is sometimes called a 'salt') . key can be any two characters (extra characters will be ignored). Only the first eight characters of plaintext will be used in the encryption. key is used as the first two characters of the encrypted value. CRYPT always yields the same result when applied to a particular plaintext and key.



Bitwise operators

These operators act on numbers at the binary or 'bit' level. For example, BITAND compares the bits of two numbers, and returns a third number whose bits are equal to 1 (turned on) only if the same bits are equal to 1 in both the original numbers. That is, it performs a logical AND on corresponding bits.

The expression '{27 BITAND 13}' is evaluated as follows: 27 has the binary (base 2) form '00011011'; 13 has the binary form '00001101'. The only bits that are equal to 1 in both numbers are the first and fourth (counting from the right); therefore, the result is the number that has those bits equal to one, and all others equal to 0: 00001001, or 9 in decimal form.

BITAND
Bitwise AND
expr_a BITAND expr_b
Perform a logical AND on the bits of expr_a and expr_b.
BITOR
Bitwise OR
expr_a BITOR expr_b
Perform a logical OR on the bits of expr_a and expr_b.
BITXOR
Bitwise Exclusive OR
expr_a BITXOR expr_b
Perform a logical exclusive OR on the bits of expr_a and expr_b (return the number whose bits are equal to 1 in either, but not both, of the original numbers).
BITOC
Bitwise ones complement
BITOC expr
Flip the bits of expr (including the 'sign bit'). Notice that this operator is unary: it acts on one number, not two. Here is an example: {BITOC 9}. 9 in binary form is '1001'; including the 'sign bit' (leftmost bit), which indicates that the number is positive, it is '01001'. Flipping these bits gives '10110'. Since the sign bit is now '1', the number is negative. According to the rules of binary arithmetic, '0110' interpreted as a negative number is '-10'.
BITSL
Bitwise shift-left
expr_a BITSL expr_b
Shift the bits of expr_a to the left by expr_b places. The leftmost bits are lost, and the rightmost bits are replaced by zeroes.
'{23 BITSL 2}' is interpreted as follows: 23 is 00010111 in binary form; shifting these bits left two places gives 01011100, or 92 in decimal format.
BITSR
Bitwise shift-right
expr_a BITSR expr_b
Shift the bits of expr_a to the right by expr_b places. The rightmost bits are lost, and the leftmost bits are replaced by zeroes.


Order of precedence for operators

Sometimes an expression can be interpreted in more than one way, depending on which operators are executed first. For example, {2 + 3 * 4} could be evaluated as 14 or 20, depending on whether the + or the * was evaluated first. In order to make the expected results unambiguous, Miva Script follows three rules:

  1. Each operator is assigned a precedence, and if there is a choice of which operator to evaluate first, the operator with higher precedence is evaluated.
  2. If two operators have the same precedence, the leftmost one is evaluated first.
  3. Sub-expressions inside parentheses, '(' and ')', are evaluated first.

The order of precedence for Miva Script operators, from highest to lowest, is as follows:

  • NOT
  • FMT, ROUND, CRYPT, MOD, SUBSTR, POW
  • /, *
  • +,-,$
  • IN, CIN, EIN, ECIN, EQ, NE, GE, LE, LT, GT
  • AND, OR

According to these rules, {2 + 3 * 4} is evaluated to 14, because '*' has a higher precedence than '+'. If you really want the '+' to be evaluated first, you can use parentheses: {(2 + 3) * 4}. In the expression {2 / 4 * 5}, both operators have the same precedence, so the leftmost one (/) is evaluated first, and the result of the whole expression is 2.5.



Macros

So far you have seen <MvEVAL> used to obtain (and display) the value of variables and expressions. Sometimes you will want to obtain and use a variable value in contexts where <MvEVAL> is not appropriate. For example, you cannot assign the value of a variable to an HTML attribute using <MvEVAL> or a Miva Script expression.

One way to use a variable value is by using it in a macro expression. This allows you to use the contents of a variable as in-line HTML or Miva Script code. The syntax of a macro is:

&[ variable_name ];

When a Miva Script tag is processed, any variables found inside '&[ ...];' will be evaluated before the rest of the tag is parsed and or executed.


Note: A macro can contain only a single variable, and no expressions, literals, or functions.

For example, suppose the variable server_name has the value www.sq.com. The following code uses the value of server name to create a URL:

<A HREF="http://&[server_name];/index.html">Home Page</A>

In this example, the value of server_name will be substituted for the '&[...];'. The tag above is equivalent to:

<A HREF="http://www.sq.com/index.html">Home Page</A>

The characters '&amp;' will also function as the start of a macro: &amp;[var1]; and &[var1]; are equivalent. The semi-colon at the end of a macro is optional, but, if you want a semi-colon to be displayed after a macro, you will have to enter it twice (for example, &[var];;). To summarize, the following four forms are equivalent:

&[variable];
&[variable]
&amp;[variable];
&amp;[variable]

The variable used in a macro can also stand for executable code. The current implementation of Miva Script does not support macros that stand for Miva Script code that involves a loop or other jump. For example:


Tip: Both expressions and macros can be used to specify the values of Miva Script attributes. Only macros can be used to specify the values of HTML attributes. This attribute assignment is correct:
<FORM ACTION="&[documenturl];">
This form is incorrect:
<FORM ACTION="{documenturl}">

Macros provide the capability for "indirection" and self-generating code:

<MvASSIGN NAME="var_a" VALUE="var_b"> 
<MvASSIGN NAME="var_b" VALUE="burrito"> 
<MvEVAL EXPR="{&[var_a];}">

'<MvEVAL EXPR="{&[var_a];}">' will display the value burrito. This may seem surprising at first, but you should consider the order in which the Miva Script code is evaluated. First, the macro substitution takes place: in this step, the expression '&[var_a];' is replaced by the current value of var_a. This is the string, not the variable, 'var_b'. The <MvEVAL...> tag becomes, in effect:

<MvEVAL EXPR="{var_b}"> 

Then the resulting <MvEVAL...> is executed. The EXPR '{var_b}' is evaluated, and its value, the string 'burrito', is displayed.

Many of the functions of macros can also be carried out using the string concatenation operator, '$'. For example:

<MvASSIGN name="firstname" value="Alex">
<MvASSIGN name="lastname" value="DeLarge">
<MvEVAL expr="&[lastname];, &[firstname];">
<MvEVAL expr="{lastname $ ', ' $ firstname"}>

The two <MvEVAL> tags in this example each display the same thing: 'DeLarge, Alex'.


Tip: If the value of a variable contains double quotes, using it in a macro as the value of an attribute may cause a syntax error. The form:
EXPR="{var}"
is safer than:
EXPR="&[var];"
In general, you should not use a macro unless you have a very good idea of the format of the value in the variable it contains. See also the following section on security.



Security issues with macros

Because Miva Script macros can be used to insert executable Miva Script code directly into a script, a potential security problem exists. If user input is displayed using a macro, and that input contains executable Miva Script code (such as calls to built-in file manipulation functions) then unexpected and undesirable results may occur. However, good programming practices can prevent this from happening.


Note: A macro in a quoted string that is inside an expression is expanded inside of the quoted string and will not be executed.

  1. All user input (for example, input from a FORM, from a data file, from the URL command line, or from a POST action) should be validated to ensure that it does not contain executable code. If it does, it must not be displayed using a macro (or if it is, it must be entity-encoded). Ideally, your script should issue an error message to the user indicating that tags must not be entered. Variations on the the following code fragment will test whether the input contains any executable code (that is, tags). In this example, assume that input from a FORM is contained in the variables field1, field2, field3, field4, and field5.
    <MvASSIGN
        NAME="input"
        VALUE="{field1$field2$field3$field4$field5}">
    <MvIF EXPR="{('<' IN input) OR ('>' IN input)}">
        <P>Invalid input! Please do not enter HTML or Miva Script tags in any field!</P>
    </MvIF>
    Entity-encoding a variable makes the contents of the variable safe and can be used instead of the code above.
  2. Use <MvEVAL> instead of a macro to display the values of variables. Macros must be used if you want to insert a variable's value in an attribute, but for displaying text to the browser a macro can always be replaced by an <MvEVAL> tag. For example:
    <MvCOMMENT>Possibly unsafe!</MvCOMMENT>
    ...
    You said: &[someinput];
    
    <MvCOMMENT>Safe!</MvCOMMENT>
    ...
    You said: <MvEVAL EXPR="{someinput}">
  3. If your program is not relying on the value of a variable being set via user input, and that variable is being displayed with a macro, the variable should be explicitly initialized (set to some initial value with <MvASSIGN>) in the program. This prevents values from other sources inadvertently or intentionally being output.


Macro encoding

The value of a macro can be URL- or entity-encoded before it is put out. This can be used to make the macro output secure.

Entity-encoding

The following construction converts all characters in the macro output that have HTML entity equivalents to those entities (this is equivalent to applying the encodeentities() function):

&[var:entities];

For example:

<MvASSIGN NAME="Step1" VALUE="Press <Enter>">
&[Step1];<BR>
&[Step1:entities];

This code will display:

Press
Press <Enter>

When &[Step1]; is evaluated, the string <Enter> is passed to the browser; since this is an unknown tag, it is not displayed. When &[Step1:entities]; is evaluated, the '<' and '>' characters are converted to entities: Press &lt;Enter&gt;. This string is displayed as intended in the browser.

URL-encoding

A string that results from a macro evaluation can be URL-encoded to make it safe to use as a name or value in a URL query string (that is, the part of a URL to the right of the '?', if there is one). In this encoding, special characters such as +, %, &, =, and space are replaced by safe equivalents. Note that encoding query strings is not identical to encoding the 'document' part of the URL (the part to the left of the '?'): for example, a space in a query string is encoded as '+', whereas in a document URL it is encoded as '%20'.

The following construction URL-encodes the macro output; this is equivalent to applying the encodeattribute() function.

&[var:attribute];

For example, if var has the value 'R&D', &[var:attribute]; produces 'R%26D'.

You should use this form of the macro if you are using the macro output as a name or value in a query string. Do not encode an entire query string: if you do, the '=' between names and values, and the '&' between pairs will be encoded and thus lose their special meanings.



Configuring the Miva processor


Summary

<MIVA
    INTERPRET="tags,macros"
    STANDARDOUTPUTLEVEL="html,text,compresswhitespace"
    DEFAULTMACROENCODING="none,entities,attributes"
    ERROROUTPUTLEVEL="syntax,expression,runtime"
    ERRORMESSAGE="text_of_error_message"
    <TAGNAME>_Error="fatal/nonfatal,display/nodisplay">

The <MIVA> tag enables you to configure the level of code interpretation, output, Default Macro Encoding, error reporting, and error handling. <MIVA> is an empty tag.


You can use as many <MIVA> tags as you need, to turn various features on and off as needed throughout your program. Each <MIVA> tag can specify one or more attributes. The STANDARDOUTPUTLEVEL and ERROROUTPUTLEVEL attributes can also be specified for individual user-defined functions.

When any <MIVA> attribute is omitted, its default setting is assumed.

The 'STANDARDOUTPUTLEVEL' attribute has an additional option. This is the 'compresswhitespace' option. When enabled, it will cause the Miva Engine to eliminate any extra whitespace in the output of Miva Script code.



Configuring Miva Script code interpretation

The Miva Script code in a program typically consists of Miva Script tags and macros. You can use the INTERPRET attribute of <MIVA> to specify whether the Miva interpreter will execute tags, macros, or both. By default (that is, if you don't have an explicit setting for INTERPRET) both are interpreted.

The possible INTERPRET values are:

  • tags: Miva Script tags will be interpreted
  • macros: Miva Script macros will be interpreted
  • tags,macros or macros,tags: both tags and macros will be interpreted (the default).
  • null string: neither tags nor macros will be interpreted. Note that an INTERPRET value consisting of any arbitrary string (such as none) is equivalent to the null string.


Configuring the output

A Miva Script program typically consists of Miva Script tags, Miva Script macros, HTML tags, and text (this includes white space such as newlines between tags). The Miva interpreter always sends the results of any Miva Script tags it interprets to the browser; you can use the STANDARDOUTPUTLEVEL attribute of <MIVA> to indicate whether or not you want the Miva processor to pass HTML tags and text to the browser. By default (that is, if you don't have an explicit setting for STANDARDOUTPUTLEVEL) both are passed to the browser.


Note: Since the Miva Script macros on each line of the document are resolved into their textual equivalents before the Miva Script tags on that line are executed, macro references are treated as just another type of text by STANDARDOUTPUTLEVEL.

The possible STANDARDOUTPUTLEVEL values are:

  • html: HTML tags will be passed to the browser
  • text: text will be passed to the browser
  • text,html or html,text: both text and HTML tags will be passed to the browser (same as the default)
  • null string: neither text nor HTML tags will be passed to the browser. Note that a STANDARDOUTPUTLEVEL value consisting of any arbitrary string (such as none) is equivalent to the null string.

For example, this setting will cause only tags to be sent to the browser:

<MIVA STANDARDOUTPUTLEVEL="html">

One of the most common uses of STANDARDOUTPUTLEVEL is preventing unnecessary and excessive white space in the output of the program: when a line of Miva Script code is executed, any spaces, tabs, or newlines on that line are passed to the browser. To avoid this (and thereby possibly speed up your output), you can set STANDARDOUTPUTLEVEL to html only. This causes all literal text in the document to be suppressed, so if there is some text that you want to display anyway, you must use an <MvEVAL> tag to display it. The output of <MvEVAL> is always passed to the browser, no matter what the value of STANDARDOUTPUTLEVEL is.


Default macro encoding

The Miva Script DEFAULTMACROENCODING attribute will assign the default encoding type to all macros. There are three values that can be assigned to the attribute:

  • none: marcos have no encoding,
  • entities : macros are entity encoded
  • attributes: macros are attribute encoded
Encoding used on an individual macro will override the default macro encoding.

Configuring error reporting

Miva Script can report three kinds of errors:

  • syntax errors: badly formed or missing tags or attributes. For example:
    <MvCOMMENT>Badly-formed tag</MvCOMMENT>
    <MvIF EXPR="{var}">Hello</MvIF 
    <MvCOMMENT>Missing attribute</MvCOMMENT>
    <MvIF>hello</MvIF>
    
  • expression errors: badly-formed expressions. For example:
    <MvCOMMENT>Missing '}'</MvCOMMENT>
    <MvEVAL EXPR="{a + b">
    <MvCOMMENT>Expected operator, none found</MvCOMMENT>
    <MvEVAL EXPR="{a b}">
  • runtime errors. For example, attempting to read (with <MvIMPORT>) from an external file that does not exist.

The ERROROUTPUTLEVEL attribute of <MIVA> enables you to specify which kinds of errors Miva Script will report. By default (that is, if you don't have an explicit setting for ERROROUTPUTLEVEL) all three types of errors are reported. The possible values are any combination of syntax, expression, and runtime, comma-separated. For example, to have only syntax and expression error messages displayed, you would use:

<MIVA ERROROUTPUTLEVEL="syntax,expression">

To turn off all error reporting:

<MIVA ERROROUTPUTLEVEL="">

You can specify a default error message that will be displayed before other, standard error messages. To do this, specify a value for the ERRORMESSAGE attribute of <MIVA>. The default is the null string. Here is an example:

<MIVA ERROROUTPUTLEVEL="runtime"
    ERRORMESSAGE="Now look what you've done! :-)">

If specified, this message will be displayed even if all forms of error reporting are turned off.

For more information on Miva Script error messages, see Configuring runtime error handling.



Configuring runtime error handling

Whenever executing a Miva Script tag results in an error, the text of the error message is assigned to a variable that has the same name as the tag, but with the _error suffix added. This text is identical to the error message that the script displays by default in the browser window. For example, errors from <MvIMPORT> will be assigned to the variable mvimport_error. Your script can test the contents of these variables and take some action based on the results of this test. For example:

<MvIMPORT FILE="bogus.dat" FIELDS="v1,v2,v3" DELIMITER=",">
....
</MvIMPORT>
<MvIF EXPR="{'cannot find the file' CIN mvimport_error}">
    <PRE>File not found.</PRE>
</MvIF>

Make sure to put the <MvIF> somewhere that it will be executed--if you were to put it inside the <MvIMPORT>...</MvIMPORT> tags, it wouldn't get executed since the <MvIMPORT> loop is not entered if the input file is not found.

This technique can be used to test the results of database operations such as <MvSKIP>, <MvFIND>, and <MvGO>.

If you want to suppress the default Miva Script error messages from being displayed in the browser window, you can use the <MIVA> tag as follows:

<MIVA ERROROUTPUTLEVEL="syntax,expression">

This setting leaves out the value 'runtime', thereby turning off runtime error messages.

You can control how Miva Script handles errors for specific tags. For each tag, <MIVA> has an attribute TAGNAME_ERROR (for example, MvIMPORT_ERROR). These attributes can have the following values:

  • fatal/nonfatal: fatal will cause all runtime errors for this tag to terminate the script; nonfatal will allow the script to continue.
  • display/nodisplay: display will cause runtime error messages for this tag to be displayed; nodisplay will suppress error messages for this tag.

For example:

<MIVA MvIMPORT_ERROR="nonfatal,nodisplay">

This will cause <MvIMPORT> runtime errors to not terminate the script or be displayed.


Note: If the ERROROUTPUTLEVEL attribute exists, its runtime setting (whether present or absent) overrides the display/nodisplay setting made with TAGNAME_ERROR.



Using JavaScript with Miva Script

JavaScript and Miva Script cannot interact directly, because they are processed at different points in the document-processing cycle: Miva Script code is pre-processed, before the document gets to the browser, whereas JavaScript is processed in the browser after the browser receives the document (which has already been processed by Miva). However, some interactions are possible since JavaScript code can be embedded in Miva Script documents (for example, in a <SCRIPT> tag, or as the target of a URL), just as it can be in regular HTML documents. Therefore:

  • You can write Miva Script code that generates JavaScript code.
  • JavaScript code can assign a value to a form field that is later passed on to a Miva Script program.
  • You can call a Miva Script program from a command line (URL) that was generated from JavaScript.

Keep in mind, though, that the JavaScript code can interact directly only with the HTML code in your document, not the Miva Script code.



Flow control

Most of the Miva Script code that you have seen so far has consisted of a series of tags that were executed in a straightforward, top-to-bottom manner. In fact, though, most programs have a more complex structure, or 'flow of control'. Program structures typically involve:



Decision-making - MvIF


Summary

<MvIF EXPR="{condition}">
...code...
</MvIF>

Process the code between the <MvIF> and </MvIF> tags if and only if condition is true. EXPR is required.

<MvIF EXPR="{condition}">
...code...
<MvELSE>
...code...
</MvIF>

Process the code between the <MvIF> and <MvELSE> tags if condition is true. Otherwise (that is, if condition is false) process the code between the <MvELSE> and </MvIF> tags. EXPR is required. <MvELSE> is an empty tag.


The <MvIF> tag lets the program test a condition and then take an action (or no action) depending on whether the condition is true or false. Here is an example that you saw in the section on expressions:

<MvIF EXPR="{age GE 17}">
    <p><b>This person is eligible to drive a motor vehicle.</b></p>
</MvIF>

In this example, the <MvIF> tests the condition '{age GE 17}' (that is, "is the value of the variable age greater than or equal to 17?"). If the condition is true, the HTML tags and text between the <MvIF> and the corresponding </MvIF> end-tag are displayed by the browser. If the condition is not true, no action is taken.

<MvIF> has one attribute, EXPR, whose value is interpreted as a conditional expression, typically one involving a comparison, string, or logical operator. The action--text, HTML, or Miva Script code that you want processed if the condition is true--must follow the <MvIF> tag. You can put as many lines as you need here. Finally, an <MvIF> tag must have a corresponding </MvIF> end tag; this comes at the end of the action.

You can nest <MvIF> tags--that is, enclose one inside another. You must make sure that each <MvIF> tag has a corresponding </MvIF> end-tag. Here is an example of nesting:

<MvIF EXPR="{age GE 17}">
    <p><b>This person may be eligible to drive a 
    motor vehicle.</b></p>
    <MvIF EXPR="{age GT 80}">
        <p><b>This person requires a re-examination.</b></p>
    </MvIF>
    <p><b>The license fee is $40.75.</b></p>
</MvIF>

In this example, if the value of age is 45, the code will be executed as follows:

  1. The first <MvIF> is evaluated.
  2. Since age is greater than or equal to 17, the program continues with the next line after the <MvIF>.
  3. This line is executed, and the text is displayed.
  4. The second <MvIF> is evaluated.
  5. Since age is not greater than 80, the next line after this <MvIF> is not executed. Instead, the program jumps to the next line after the </MvIF> end-tag that corresponds to the second <MvIF>.
  6. This line is executed, and the text is displayed.

The result will be:

This person may be eligible to drive a motor vehicle.
The license fee is $40.75.

Either-or decisions - MvELSE

With the simple <MvIF> described above, the program can choose whether to carry out an action. Often, though, there will be two possible actions that you might want the program to take, one if a condition is true, and another if the condition is false. In this situation, you would use the <MvELSE> tag. For example:

<MvIF EXPR="{age GE 17}">
    <p><b>This person is eligible to drive a motor vehicle.</b></p>
<MvELSE>
    <p><b>This person is too young to drive a motor vehicle.</b></p>
</MvIF>

In this example, if age is greater than or equal to 17, the first paragraph will be displayed. If age is less than 17, the second paragraph will be displayed.

<MvELSE> consists of a single tag, with no attributes. It divides the <MvIF> into two 'branches'--the code to execute if the condition is true, and the code to execute if the condition is false. If the condition is true, the code between the <MvIF> and the <MvELSE> is executed; otherwise, the code between the <MvELSE> and the </MvIF> is executed. The syntax for using <MvELSE> is:

<MvIF EXPR="{expr}">
...actions for "true" case...
<MvELSE>
...actions for "false" case...
</MvIF>

Multiple decisions

Sometimes the possibilities that a program has to deal with cannot be described in either/or terms--there may be three, four, or more possible situations, each requiring a different action. In Miva Script code this kind of decision making is represented with multiple, nested <MvIF>-<MvELSE>-</MvIF> tags. Here is an example:

<MvIF expr="{age LT 17}">
   <p>This person is too young to drive a motor vehicle.</p>
<MvELSE>
   <MvIF expr="{age GE 17 AND age LE 114}">
        <p>This person is eligible to drive a motor vehicle.</p>
   <MvELSE>
        <p>This person is too old to drive a motor vehicle.</p>
   </MvIF>
</MvIF>

Here there are three cases that the program has to deal with:

  • age is less than 17
  • age is between 17 and 114, inclusive
  • age is greater than 114

The first case is covered by the first 'branch' of the first <MvIF>. If age is not less than 17, the program jumps to the second (<MvELSE>) branch of the <MvIF>. The code in this branch is another <MvIF>, which covers the other two cases. If age is between 17 and 114, the first branch is executed, otherwise, the second branch of the second <MvIF> is executed.

You can nest <MvIF>-<MvELSE>-</MvIF> tags to as many levels as you like, as long as you match up the <MvIF> and </MvIF> tags properly.



Repetition - MvWHILE


Summary

<MvWHILE EXPR="{expression}">
    ...code...
....<MvWHILESTOP>
    ...code...
</MvWHILE>

Repeats the code between <MvWHILE> and </MvWHILE> until the condition expression, given by the required attribute EXPR, is false, or an <MvWHILESTOP> (optional) is executed. <MvWHILESTOP> is an empty tag.


Many programming tasks require a series of steps to be repeated several times. Sometimes the number of repetitions is known in advance; other times the repetitions continue until a particular condition is true. This activity is called a loop; in Miva Script, general-purpose loops are carried out with <MvWHILE>...</MvWHILE> tags. (Some other tags, such as <MvIMPORT> and <MvCALL>, also loop through their input.)

<MvWHILE> has one attribute, EXPR, whose value will be interpreted as a condition. The code (text, HTML, and Miva Script tags) between <MvWHILE> and </MvWHILE> will be repeated as long as the condition expr is true. You can put as many lines of code as you need here.

Repeating a fixed number of times

Here is an example that displays the first five positive 'square' integers:

<MvASSIGN name="num" value="1">
<MvWHILE EXPR="{num LE 5}">
    <MvEVAL EXPR="{num * num}"><br>
    <MvASSIGN name="num" value="{num + 1}">
</MvWHILE>

This code displays:

1
4
9
16
25

            

This code starts by assigning an initial value for the variable num. In this context num functions as a counter, a variable that is used to count how many times the loop repeats. Since we know exactly how many times to repeat, we can construct a condition to reflect that. In this case we use the condition '{num LE 5}', that is, the loop will repeat while num is less than or equal to 5.

The <MvEVAL> tag evaluates and displays the square of num (num multiplied by itself). Then, since we are counting loop repetitions, the value of num is increased by 1 using <MvASSIGN>. The program then jumps back to the <MvWHILE> tag and tests the condition again. As long as num is less than or equal to 5, the loop repeats. These steps are carried out 5 times; at that point the value of num is 6, so the next time the condition is tested, it is found to be false, and the loop is not repeated. The program then jumps to the next line after the </MvWHILE> tag.

This example uses a common programming technique--setting up a counter that is incremented each time the loop is repeated, and then testing that counter to decide whether the loop should keep on repeating. Common programming mistakes are forgetting to increment the counter inside the loop, and not setting it to the correct initial value before the loop starts. Note that in this case the value of the counter is used in other calculations inside the loop; this is not always the case.

Repeating a non-fixed number of times

The 'counter' technique can be used if you know exactly how many times you want the loop to repeat. Often you do not know this--the number of repetitions depends on some condition that will not be achieved in a predictable (or easily predictable) number of steps. For example, it may depend on input from a user or from a data file.

In this case you have to set up your loop condition as described above, but now the condition depends on something other than a counter. You have to make sure that which ever variable(s) the loop condition tests are ones that are affected by the code inside the loop. A common programming mistake is to set up a loop condition that is always true because that condition is never affected by what goes on inside the loop.

Terminating a loop with <MvWHILESTOP>

Processing can be halted inside an <MvWHILE> ...</MvWHILE> loop by using the <MvWHILESTOP> tag. This will stop execution of the code inside the loop. The program then jumps to the first line after the </MvWHILE>

<MvASSIGN NAME="num" VALUE="0">
<BLOCKQUOTE>
<MvWHILE EXPR="{num LE 5}">
    <MvASSIGN NAME="num" VALUE="{num + 1}">
    This loop has executed 
    <MvEVAL EXPR="{num}"> times.<BR>
    <MvIF EXPR="{num GE 3}">
        <MvWHILESTOP>
    </MvIF>
</MvWHILE>
<B>Finished</B>
</BLOCKQUOTE>

Since the condition governing this loop is '{num LE 5}' you might expect the loop to be repeated five times. In fact, though, the loop repeats only three times because when the condition '{num GE 3}' is met, the <MvWHILESTOP> is executed, and the loop terminates.



Exiting from a program - MvEXIT


Summary

<MvEXIT>

The <MvEXIT> tag causes the script to terminate. <MvEXIT> is an empty tag.



Note: An <MvEXIT> in a script called with <MvDO> also causes the script containing the <MvDO> to terminate. To exit from a script called by <MvDO>, without exiting from the calling script, use <MvFUNCRETURN>.



Passing data to a program

There are several ways to pass data to a Miva Script program, or from one Miva Script program to another:

Data passed in this way are referred to as arguments. Miva Script will use both conventions to interpret any data passed to a program; we recommend that you choose one or the other for each program and use it consistently within that program.

Data passed using these methods are always passed as name/value pairs.

You can also pass data:


Note: Unless a Miva Script variable is passed to another program using one of the methods listed above, it has no meaning outside the currently running instance of the program; that is, its maximum scope is the current program instance.



Name/value pairs

Data passed on the URL command line are referred to as arguments. Miva will interpret any data passed to it in this way as both name/value pairs and as an value list. In general it is not a good idea to code the program so that it uses both conventions: we recommend that you choose one or the other for each program and use it consistently within that program.

You can pass data to a Miva Script program using the same syntax that a browser uses to submit a FORM with a METHOD of GET.

Here is an example using the CGI version of Miva Empresa:

http://www.server.com/cgi-bin/miva?fruit.mv+fruit1=black+cherries&fruit2=oranges

Here is an example using the NSAPI version of Miva Empresa:

http://www.server.com/fruit.mv?fruit1=black+cherries&fruit2=oranges

With Miva Mia, this would be:

http://127.0.01/fruit.mv?fruit1=black+cherries&fruit2=oranges

These examples pass two variable values to the program fruit.mv: fruit1 has the value "black cherries", and fruit2 has the value "oranges". The fruit.mv program can use these variables with the values specified.

Using this syntax, each variable name and its corresponding value must be separated by an equal, '=', sign. Each name/value pair is separated by an ampersand, '&'. Spaces are replaced by a '+' sign, and some special characters (notably newline, '=', and '&') are replaced by '%nn', where the ns are digits from 0-9 and/or letters between 'A' and 'F'.

This method of receiving arguments allows a Miva Script program to process an HTML form submitted with a METHOD of GET.

If a variable name is submitted without a value, the value will default to the name itself.

Some characters cannot be used safely in arguments passed as a list of name/value pairs, but you can replace them by their URL-encoding. These are the most common such characters:

Character URL-encoding
Space ( ) +
Plus sign (+) (used to represent itself) %2b
Percent (%) %2f
Equal (=) %3d
Ampersand (&) %26


Value list

Arguments can also be passed as a simple list of values, separated by plus signs. Miva Empresa will automatically generate variable names corresponding to these values.

Here is an example of calling a program with arguments using the CGI version of Miva Empresa:

http://www.server.com/cgi-bin/miva?fruit.mv+black%20cherries+oranges

Here is an example using the NSAPI version of Miva Empresa:

http://www.server.com/fruit.mv?black%20cherries+oranges

With Miva Mia, this would be:

http://127.0.01/fruit.mv?black%20cherries+oranges

With Miva Empresa (CGI) arguments are passed using the convention file.mv+arg2+arg3+arg4...; that is, arguments follow the filename and are separated by plus signs (+). With Miva Empresa (NSAPI) and Miva Mia the convention is file.mv?arg2+arg3+arg4...; arguments are separated from the filename by '?' and after that and are separated from each other by plus signs (+). In this example, the arguments are black%20cherries and oranges. The notation '%20' is used to represent a space character, because spaces cannot appear explicitly in URLs (see below).

The model given above starts with arg2 because of the way Miva Script counts its arguments. The Miva processor automatically creates two types of variables from these arguments. A single variable, with a numerical value, called nargs is created: this variable contains the number of arguments passed to the program. Secondly, a series of variables called arg1, arg2, ..., argN (where N equals nargs, the number of arguments) is created. Each of these will contain the value of the corresponding argument passed to the program in the URL. arg1 is always the name of the program itself (and therefore nargs is always at least 1). The actual arguments start at arg2. Your program code can then do whatever you need with these arguments.

In the example above:

nargs=
arg1=
arg2=
arg3=
3
fruit.mv
black%20cherries
oranges

The following characters cannot be used safely in arguments passed as a value list, but you can replace them by their hexadecimal equivalents, as indicated:

Character Hexadecimal form
Space ( ) %20
Plus sign (+) %2b
Minus sign/hyphen (-) %2d
Tilde (~) %7e

For example:

http://127.0.0.1/fruit.mv?granny%20smith%20apples+oranges

Note: If you pass a null argument (an argument with no value), Miva Script will readjust the list of argument values to ignore the null argument. For example:
http://www.your_server.com/cgi-bin/miva?fruit.mv++oranges
The first argument after the script name is absent. In this example, 'oranges' will become arg2 instead of arg3.

If a Miva Script program is the target of a FORM's ACTION, the values of fields used in the form will be converted into Miva Script variables with the same names as the fields.



Passing variables - MvHIDE


Summary

<FORM ACTION="..." METHOD="GET|POST">
...
<MvHIDE FIELDS="var1,var2,var3,...">
...
</FORM>

<MvHIDE> tags are converted into one or more 'hidden' <INPUT> tags, corresponding to each of the variables var1, var2, var3 specified by FIELDS (required).

If variable var1 has value x, and var2 has value y (x and y are literals), then:

<MvHIDE FIELDS="var1,var2">
is converted in place to:
<INPUT TYPE="hidden" NAME="var1" VALUE="x">
<INPUT TYPE="hidden" NAME="var2" VALUE="y">

<MvHIDE> tags should be used only inside forms. When the form is submitted, the variables and their values are passed to the receiving program (specified by the <FORM> element's ACTION attribute) as name/value pairs (if METHOD is 'GET') or in the standard input (if METHOD is 'POST'). This provides a way to pass variable values to the program called by the form.


By default, when a Miva Script program calls another Miva Script program (which could be a re-invocation of the same program) the values assigned to variables in the currently running program are not available in the program that is called. Another way of expressing this is to say that the scope of a variable is the currently running program.

As described above, one way to pass variables to a Miva Script program is to include them as name/value pairs on the URL by which the program is called. This isn't always convenient, since you may not know when you construct the URL which values and variables you want to pass. Miva Script provides the <MvHIDE> mechanism to make it easy for programs to pass variables to other Miva Script programs.

Recall that if a Miva Script program is the target of a FORM's ACTION, the values of fields used in the form will be converted into Miva Script variables with the same names as the fields, and these variables and their values will be available in the target program. Normally these fields are visible objects such as text boxes and radio buttons, whose values are entered by the user. HTML also provides so-called 'hidden' form fields: these fields have a name and value like other form fields, but have no visual representation in the browser. Hidden fields can be used to pass variable values to a Miva Script program; <MvHIDE> provides a convenient method of generating hidden fields 'on the fly', when the form is submitted. In general, if you have variables v1, v2, v3, ..., vN whose literal values are x1, x2, x3, ..., xN, then an <MvHIDE> tag of the form <MvHIDE FIELDS="v1,v2,v3,...,vN"> will be converted to the following when the form is submitted:

<INPUT TYPE="hidden" NAME="v1" VALUE="x1">
<INPUT TYPE="hidden" NAME="v2" VALUE="x2">
<INPUT TYPE="hidden" NAME="v3" VALUE="x3">
...
<INPUT TYPE="hidden" NAME="vN" VALUE="xN"> 

For example, if v1 has the value 6, v2 the value 8, and v3 the value 10, then:

<MvHIDE FIELDS="v1,v2,v3">

Is converted to:

<INPUT TYPE="hidden" NAME="v1" VALUE="6">
<INPUT TYPE="hidden" NAME="v2" VALUE="8">
<INPUT TYPE="hidden" NAME="v3" VALUE="10">

The variables do not have to be originally specified as literals: Miva Script resolves the literal value when it processes the <MvHIDE> tag.

<MvHIDE> has no useful effect if it occurs outside a form.


Note: Variables corresponding to fields that are already part of the form should not also be passed to the receiving program using <MvHIDE>.



Using cookies

Each time a cookie-enabled browser accesses a Miva Script document, Miva Script creates a 32-character cookie, with the name htscallerid, that is unique to that browser and URL. The cookie lasts for one year after being set. Cookies can be turned off in the Miva Empresa configuration file; contact your server administrator. The content of the cookie are available in the callerid system variable.

If you want to create your own cookies, you can use the <META> tag (Miva Script does not let you modify the HTTP headers sent with your document). For example:

<META HTTP-EQUIV="Set-Cookie" 
    CONTENT="id=MyCo; expires=Thu, 26 Nov 1998 16:00:00 GMT;
             domain=.sq.com path=/; secure">

The HTTP-EQUIV attribute must have the value Set-Cookie, to indicate that the <META> is simulating the Set-Cookie HTTP header. CONTENT specifies the content of the cookie. This can have five parts, separated by semi-colons (;).

  • A name/value pair (id=MyCo in this example) is mandatory. The name and value are arbitrary strings, but cannot contain a comma, semi-colon, or space.
  • You can optionally specify an expiry date. The date can be in one of these forms, of which the first is preferred:
    Day, DD Mon YYYY, HH:MM:SS GMT (Thu, 05 Nov 1998 16:00:00 GMT)
    Day, DD-Mon-YY HH:MM:SS GMT (Thursday, 05-Nov-98 16:00:00 GMT)
    Day Mon D (or DD) HH:MM:SS YYYY (Thu Nov 5 16:00:00 1998)
  • You can optionally specify to which domain the cookie can be sent, and to which URLs (paths) within that domain.
  • You can optionally specify that a secure protocol be used to send the cookie.

All of the cookies for this document are available in the http_cookie system variable.



Functions


Summary

<MvFUNCTION NAME="myfunc" 
    PARAMETERS="var1,var2,..."
    STANDARDOUTPUTLEVEL="html,text"
    ERROROUTPUTLEVEL="syntax,expression,runtime">
    ...code... 
    <MvFUNCRETURN VALUE="{expression}">
    ...code... 
</MvFUNCTION>
...
myfunc(val1,val2,...)

Defines the function myfunc, whose name is specified with the required attribute NAME. Parameters var1, var2, ..., can optionally be defined with the PARAMETERS attribute. These are assigned the values va11, val2, ..., in the function call. The function can optionally terminate with a return value specified with <MvFUNCRETURN>. Functions can use locally defined variables whose scope is the function body. STANDARDOUTPUTLEVEL and ERROROUTPUTLEVEL are optional and are interpreted in the same way as in the <MIVA> tag. <MvFUNCRETURN> is an empty tag.

Functions are called in expressions. Functions can be forward referenced: Miva Script code can make references to functions that are defined after the reference in question.


Sometimes you will need to carry out the same series of steps at several different locations in your program. If you were to repeat the same lines of code every place they were needed, your program would get long, hard to read, and hard to modify, and the chances of making errors would increase. Functions enable you to write re-usable code that can be called on to perform operations throughout the program, by calling the function in any valid expression. Even if a function is only called once in your program, moving the code out of the main body of the program and replacing it with a function call still helps to make the program more 'modular' and easy to follow.

Here is an example--a function that returns the greater of two numbers:

<MvFUNCTION name="greater2" parameters="num1,num2">
<MvIF EXPR="{num1 GE num2}">
    <MvFUNCRETURN value="{num1}">
<MvELSE>
    <MvFUNCRETURN value="{num2}">
</MvIF>
</MvFUNCTION>
...
...
<MvASSIGN name="older" value="{greater2(age1,age2)}">

This example defines a function called greater2 and shows how it is used in an <MvASSIGN> tag. The point of the <MvASSIGN> is to get the greater of the values in the two variables age1 and age2 and assign that value to the variable older. The value of the <MvASSIGN> is a reference or 'call' to the function greater2. The variables age1 and age2 appear in brackets after the function name; they are called the 'arguments' of the function and are said to be 'passed to' the function.

Now consider the function definition. Functions start with <MvFUNCTION> and end with </MvFUNCTION>. The NAME attribute of <MvFUNCTION> is required, and specifies the name by which the function is called.

To understand the PARAMETERS attribute, look again at the call to greater2 in the <MvASSIGN> tag; this call asks greater2 to return the greater of age1 and age2, however, as you can see these two variable names don't appear anywhere inside the definition of greater2. Instead, the values of age1 and age2 are assigned to the 'parameters' of greater2: these are called num1 and num2 and are defined by the PARAMETERS attribute. Parameters are variables whose scope is the body of the function, that is, they are not meaningful anywhere outside the function. When the function is called, the parameters are automatically assigned the values of the arguments that are specified when the function was called. These assignments are done in the order that the arguments appear: in this example, the value of age1 is assigned to num1, and the value of age2 is assigned to num2.

There are two reasons why the function uses parameters instead of using age1 and age2 directly:

  • You might want to call greater2 with different variables as its arguments; if the function always used just age1 and age2, you would always have to call it with those variables.
  • Since the function isn't supposed to change the values of age1 and age2 it's safer not to use these variable names in the function. If you unintentionally changed one of these values in the function, it might cause a problem that would be hard to track down later.

Let's return to the definition of the function: it consists of a single <MvIF>-<MvELSE>-</MvIF> block. If num1 is greater than or equal to num2, then the first <MvFUNCRETURN> tag is executed. The purpose of this tag is to 'return' or send a value back to the tag that called the function in the first place. This value is said to be the 'value of the function'. In this case, the function was called inside an expression in the VALUE attribute of an <MvASSIGN>; when the function returns a value, it is the same as if that value were substituted for the call to the function. Here is the <MvASSIGN> again:

<MvASSIGN name="older" value="{greater2(age1,age2)}">

Suppose age1 equals 17 and age2 equals 12. When greater2 returns the value 17, it is the same as if the <MvASSIGN> were:

<MvASSIGN name="older" value="{17}">

If num2 is greater than num1 when the function is executed, then the second <MvFUNCRETURN> returns a value. Once an <MvFUNCRETURN> is executed, the function immediately stops executing (this includes any loops such as <MvWHILE> and <MvIMPORT>) and the program jumps back to the location of the function call.

Local variables and parameters

In addition to parameters defined with the PARAMETERS attribute, you can use 'local' variables that are meaningful only inside the function. These variables must start with the l. or local. prefix. For example:

<MvFUNCTION name="func1">
<MvASSIGN name="l.var1" value="1">
<MvASSIGN name="local.var2" value="10">
...
</MvFUNCTION>

All parameters will automatically become local variables, but you should not use the l. or local. prefix directly in the parameter list:

<MvCOMMENT> Avoid this! </MvCOMMENT>
<MvFUNCTION NAME="funk" PARAMETERS="l.foo,l.bar">
...
</MvFUNCTION>

<MvCOMMENT> Correct usage. </MvCOMMENT>
<MvFUNCTION NAME="funk" PARAMETERS="foo,bar">
...
...<MvEVAL EXPR="{l.foo $ l.bar}">...
...
</MvFUNCTION> 


Calling functions

A function can be called in any expression. The simplest way to call a function is in an <MvEVAL> tag:

<MvEVAL EXPR="{func1(var_a)}">

Function calls can be combined with other components of an expression:

<MvEVAL EXPR="{'The answer is: ' $ func1(42)}">
<MvEVAL EXPR="{square(4) + square(5)}">

The argument(s) of a function can be literal, variable, or any valid expression; an expression inside a list of arguments will be evaluated before the function call takes place, and the function will be called with the resulting value.

<MvEVAL EXPR="{func1(6)}">
<MvEVAL EXPR="{func1('xyzzy')}">
<MvEVAL EXPR="{func1(a + b)}">
<MvEVAL EXPR="{func1('Dear ' $ customer $ ',')}">

The function is called before EXPR is evaluated, and the function result (the value returned by <MvFUNCRETURN>) is substituted in the expression.


Tip: If a function returns a value, but you don't want to display this value in your output, you can use <MvASSIGN> instead:

<MvASSIGN NAME="do_this" VALUE="{func1(var_a)}">

This calls the function and assigns the return value (if there is one) to the variable do_this. You can just 'throw away' do_this (that is, don't use it for anything). You may find it useful to use the same variable name--do_this, do, junk, or a name of your choice--for this purpose to make it easier to keep track of what this assignment tag is really doing.


Calling functions from a form

You can also indirectly call a function via a form submission. That is, you cannot call a function directly in this way, but you can put code in your program that takes the results of a form submission and makes a function call. Consider this example:

<BODY>
<H1>This is a test of calling a function</H1>
<MvIF EXPR = "{ fname }">
    <MvEVAL EXPR = "{ &[ fname ];() }">
<MvELSE>
    <MvEVAL EXPR = "{ main() }">
</MvIF>
<MvFUNCTION NAME="funk">
...code...
</MvFUNCTION>
...other function definition code...
<MvFUNCTION NAME="main">
...code...
<FORM ACTION="&[ documenturl ];fname=funk" METHOD="post">
...code...
</FORM>
...code...
</MvFUNCTION>
</BODY>

This example has three main parts: a function called main, a section of function definitions (funk is the only one that's shown), and an <MvIF> tag that decides which function will get called when the document is loaded.

As the name suggests, main is the part of the program that controls the other functions. Inside main there is a FORM element; the ACTION attribute of the FORM is a URL that tells the browser what to do when the user submits the form. In this case the ACTION consists of &[documenturl ];fname=funk. documenturl is a special built-in variable that always consists of the URL of the current document, with a plus sign (+) or question mark (?) appended, depending on which flavor of Miva Engine you are using . Suppose the current document's URL is:

http://www.your_server.com/cgi-bin/miva?testy.mv

The expression '&[ documenturl ];fname=funk' will be equal to:

http://www.your_server.com/cgi-bin/miva?testy.mv+fname=funk

As you saw in the section Passing data to a program, you can pass variables to a Miva Script program using name/value pairs in the URL. In this example, the program will be passed the variable fname with the value "funk".

Now look at the <MvIF> tag at the top of the document. This tag tests whether fname has a non-null value. If it does, then the expression {&[ fname ];()} is evaluated. If fname has the value "funk", the expression to be evaluated is {funk()}. As you've seen, this will call the function funk. This mechanism is how you can use a FORM action to call a function. Look also at the other branch of the <MvIF>; if fname is null, then {main()} will be evaluated, that is, the function main will be called. This is so that the first time you load the document, with no arguments at the end of the URL, the "main" part of your program will get executed.



Further information on functions

Here are a few more things you should be aware of:

  • Function code is not executed until it is called. When the Miva Script file is processed, the function definitions are read, but not actually executed until there is a function call.
  • If you pass a text string as an argument to a variable, it should be in single quotes:
    func1(1,2,'banana')
  • While defining parameters is often helpful, it is not mandatory. For example, you may want to update a 'global' variable directly.
  • An <MvFUNCRETURN> tag is also not always needed; you would use it if you needed to return a value to the place where the function was called.
  • In addition to parameters defined with the PARAMETERS attribute, you can use 'local' variables that are meaningful only inside the function. These variables must start with the l. or local. prefix. For example:
    <MvFUNCTION name="func1">
    <MvASSIGN name="l.var1" value="1">
    <MvASSIGN name="local.var2" value="10">
    ...
    </MvFUNCTION>
  • Miva Script supplies a large number of built-in functions to help you carry out time, string, numeric, and filesystem operations. Built-in functions are used in exactly the same way as user-defined functions, but generally run faster than ordinary Miva Script code.
  • A recursive function call occurs when a function calls itself. There is a limit on the number of recursive function calls at run-time. This limit is fixed at 23 for Miva Mia; the default limit for Miva Empresa is 23 by default, but this can be changed by the server administrator via the maxfunctiondepth setting in the global configuration file.


Including and running external files


Summary

<MvDO
    FILE="filename"
    NAME="var"
    VALUE="{func(args)}">

A value for FILE is required. If NAME and VALUE are specified, the function func defined in the file filename is run with arguments args and the result is assigned to the variable var. If NAME and VALUE are omitted, the file filename is processed as if it were included in the current file. In each case, any output from the external file is merged into the current file. filename can be a path: if it is a relative path, it is interpreted as relative to the current file; if it starts with a forward slash (/), it is relative to the Miva Script document root directory. <MvDO> is an empty tag.


<MvDO> works in two somewhat different ways, depending on whether NAME and VALUE are present. If they are, then the specified function is the only code in the external file that gets executed. Any other Miva Script and HTML code is ignored. If NAME and VALUE are omitted, everything in the external file is executed, and the results of all <MvASSIGN> tags are available to subsequent code in the current file; however, any function definitions in the external file are not available after the <MvDO> has been processed.

All system and global variables, and all open databases, are available to the code in the external file.


Note: If the required attribute FILENAME is missing, a value (error message) for the variable mvdo_error is set, and the script continues processing.


<MvDO> tags can be nested in the sense that the file filename specified by the <MvDO> can itself contain an <MvDO>. The filename path specified by the "nested" <MvDO> is relative to the location of the file containing that <MvDO> (unless it starts with a forward slash, it which case it is relative to the Miva Script document root directory).


Note: There is a limit on the number of nested MvDO calls at run-time. This limit is fixed at 23 for Miva Mia; the default limit for Miva Empresa is 23 by default, but this can be changed by the server administrator via the maxfunctiondepth setting in the global configuration file.


Miva does not interpret server-side includes (SSI). <MvDO> can simulate the effect of an SSI of the type used to include another document in the current document. For example:

<!--#include file="external.html"-->

This can be replaced by:

<MvDO FILE="external.html">

Similarly:

<!--#include virtual="/external.html"-->

Can be replaced by:

<MvDO FILE="/external.html">


Multiple passes through a program

If your program reads user input, you will have to re-submit the document to the browser in order for this data to be processed by the program. In the section on calling functions you saw how this can be done by specifying a function call in a form action. Another technique is to test the value of a variable passed from the form. Here is an example:

<body>
<h1>Say anything</h1>
<MvIF EXPR = "{ speak EQ '' }">
    <FORM METHOD="POST">
       <p><b>I say: <input type="text" name="speak"></b></p>
       <input type="submit" name="foo" value="Send">
       </FORM>
<MvELSE>
       <p><b>You said: <MvEVAL EXPR="{speak}"></b></p>
</MvIF>
</body>

In this example the variable speak controls the flow of the program. The first time the document is opened, speak will have no value, so the condition '{speak EQ ' '}' will be true and the following form will be displayed:

I say:

If the user enters 'My word' into the text box (which has the name speak) this text will be assigned to the variable speak when the form is submitted. On the next pass through the program, the condition '{speak EQ ' '}' will be false, and the second branch of the <MvIF> will be executed, displaying:

You said: My word

The METHOD attribute of FORM must have the value POST. Since you are not calling a specific function, it is not necessary in this case to specify an ACTION for the FORM.



Using external data files

Miva Script provides tags that enable programs to read from and write to external text data files. Any external files that you read or write must be located in your data directory. Data files can have any file extension, though .dat is the most common.



Reading from external files - MvIMPORT


Summary

<MvIMPORT 
    FILE = "filename" 
    FIELDS = "var1,var2,var3,..." 
    DELIMITER = "chars" 
    FILTER = "{expression}"> 
...
<MvIMPORTSTOP>
...
</MvIMPORT>

<MvIMPORT> imports a record (a single line) of data fields, separated by the character(s) chars, from the file filename, and stores the data in the var1, var2, var3,... variables. FILE, FIELDS, and DELIMITER are required. Optionally, a condition can be specified with FILTER, so that records that do not match the condition will be discarded. The data can be processed with the code between the <MvIMPORT> and </MvIMPORT> tags. <MvIMPORT> will loop through the entire file, stopping when the end of the file is reached or an <MvIMPORTSTOP> is executed. The variable recno is automatically created, and contains the current record number in the input file, starting at 1. <MvIMPORT> tags can be nested to read data from multiple files within the same block of code. Records in data files can be terminated by line feeds, carriage returns, or line feed/carriage return pairs. Data files should end with a blank line. <MvIMPORTSTOP> is an empty tag.



Tip: Since searching in a text file is much slower than searching in a database, we recommend that if you will be reusing the data in a text file, you import it, store it in a Miva database, and perform searches on the database. This will speed up your scripts and avoid overloading your server.

In addition to taking input from users via forms, programs can read from data files. Suppose you have a data file called movies.dat that is in the following format:

Cape Fear|Martin Scorsese|1991
The Trouble With Harry|Alfred Hitchcock|1955
Dr Strangelove|Stanley Kubrick|1963
Strange Days|Kathryn Bigelow|1995
Clerks|Kevin Smith|1994

This is a simple text file in which each line (sometimes called a record) contains a film title, the director, and the year of release. Each piece of data is called a field; the fields on each line are separated by delimiters--in this example the delimiter is the '|' (vertical bar) character.

The <MvIMPORT> tag can be used to read in this file, line by line, and process the data in some way--for example, displaying it in a table.

<TABLE>
<TR><TH></TH>
    <TH>Title</TH>
    <TH>Director</TH>
    <TH>Year</TH></TR>
<MvIMPORT 
    FILE="movies.dat" 
    FIELDS="title,director,year" 
    DELIMITER="|">
<TR><TD><MvEVAL EXPR="{recno}">.</TD>
    <TD><MvEVAL EXPR="{title}"></TD>
    <TD><MvEVAL EXPR="{director}"></TD>
    <TD><MvEVAL EXPR="{year}"></TD>
</TR>
</MvIMPORT>
</TABLE>

This code will generate a table, row by row, and fill each row with data from a line of the data file.

There are certain parts of the table code that should appear only once, so these are placed outside the <MvIMPORT>...</MvIMPORT> loop. The <TABLE> and </TABLE> tags, and the first table row (<TR>), which displays the table header, are in this category.

The attributes of the <MvIMPORT> tag tell it to do the following:

  • FILE="movies.dat": Read a line of data from the file movies.dat.
  • DELIMITER="|": Assume that the fields in each line are separated by the character '|' (vertical bar).
  • FIELDS="title,director,year": Save the first three fields of data in the variables title, director, and year, respectively. (If there are more fields in the line than there are variables specified, the extra fields are just discarded.)

If this code were reading from the sample data shown above, the variables would have the following values after the first line of data was read:

title=
director=
year=
Cape Fear
Martin Scorsese
1991

The next line of code inserts these values into a table row by displaying the values of the variables title, director, and year. The automatically-created variable recno will contain the current record (line) number, 1. The table row produced by the resulting HTML code will look something like this:

1. Cape Fear Martin Scorsese 1991

Since the <MvIMPORT>...</MvIMPORT> block is, in effect, a loop, it will repeat this process for every line of data in the file, stopping when it reads the last line of data. The final table will look like this:

Title Director Year
1. Cape Fear Martin Scorsese 1991
2. The Trouble With Harry Alfred Hitchcock 1955
3. Dr Strangelove Stanley Kubrick 1963
4. Strange Days Kathryn Bigelow 1995
5. Clerks Kevin Smith 1994

Filtering the input

Sometimes you might be interested only in input data that satisfies a particular condition. In the previous example, you might be interested only in movies from 1995. Using the FILTER attribute, you can specify a condition that the input data must meet in order for <MvIMPORT> to pass it on to the program. You could modify the previous <MvIMPORT> as follows:

<MvIMPORT 
    FILE="movies.dat" 
    FIELDS="title,director,year" 
    DELIMITER="|"
    FILTER="{year EQ '1995'}">

When <MvIMPORT> reads a line of data, it will now check that the value assigned to the variable year equals '1995'. If it doesn't, the data is discarded and <MvIMPORT> reads the next line. No processing is done on the discarded data.

The result of this <MvIMPORT> with the data file movies.dat would be:

Title Director Year
1. Strange Days Kathryn Bigelow 1995

Terminating the import

Importing can be halted explicitly inside an <MvIMPORT> loop using the <MvIMPORTSTOP> tag. The program jumps to the code following the </MvIMPORT> tag, without reading any more input from the data file.

Using the record number (recno)

The variable recno, containing to the current line number in the file being read, is generated automatically by <MvIMPORT>. A variable named recno is also generated and updated when the program is navigating in a database, and refers to the current record number in that database. If you are using both <MvIMPORT> and a database at the same time, you should disambiguate the two recno variables by using g.recno to refer to the <MvIMPORT> recno, and db_alias.d.recno (where db_alias is the database alias) to refer to the database recno.

Nested imports

It is possible in Miva Script to nest <MvIMPORT> tags. For example, you could have an 'outer' <MvIMPORT> that reads in a list of data file names from a 'master' data file, and an 'inner' <MvIMPORT> that, for each iteration of the outer <MvIMPORT>, reads the records of the data file whose name was read. The value of recno always refers to the record number in the current nesting level.



Writing to external files - MvEXPORT


Summary

 <MvEXPORT 
    FILE="filename" 
    FIELDS="var1,var2,var3,..." 
    DELIMITER="chars">

Writes the values of variables var1, var2, var3,..., to the external file filename, separated by the characters chars. FILE, FIELDS, and DELIMITER are all required attributes. DELIMITER can be one or more characters. <MvEXPORT> is an empty tag.


The <MvEXPORT> tag writes a single line of data to an external output file. For example:

<MvEXPORT
    FILE="workers.dat"
    FIELDS="name,title,dept,salary"
    DELIMITER="##">

This tag will write a line such as the following to the file workers.dat:

Frank Flash##V.P. of Silence##Admin##120000

When <MvEXPORT> writes a line of data, it always adds it to the end of the output file, and it always starts on a new line.

Unlike <MvIMPORT>, <MvEXPORT> does not loop automatically. If you want to write multiple output lines, you will have to put the <MvEXPORT> tag inside an <MvWHILE> loop.

If the data file named by the FILE attribute does not exist, then it is created when the <MvEXPORT> tag is executed.



File locking - <MvLOCKFILE>


Summary

<MvLOCKFILE FILE="filename">
...
</MvLOCKFILE>

<MvLOCKFILE> indicates to other processes that the current process has requested an exclusive lock on filename (required). A lock request is in effect until the corresponding </MvLOCKFILE> end-tag is reached or the process ends or times out. Multiple lock requests with <MvLOCKFILE> on the same file are queued. There is no limit on the number of files on which lock requests can be made. filename can be any file--a database, memo, index, or flat (.dat, .txt, etc.) file. <MvLOCKFILE> tags can be nested.



Note: In this section, it is useful to make a distinction between a 'program' and a 'process'. The same program (file containing Miva Script code) can be running more than once simultaneously on the same server: each such instance of a running program is referred to as a 'process'. Often, an instance of a program will be 'competing' with other instances of the same program (that is, other processes) for access to the same files.

<MvLOCKFILE> provides a method for the currently running program to indicate to other processes (which could be other running programs, or other instances of the same program) that it has requested exclusive access to a file. It is important to understand that a single <MvLOCKFILE> by itself does not cause the file to be locked: rather, if two processes are using <MvLOCKFILE>, and one process requests a lock, and then the second process requests a lock on the same file, then the second process is prevented from continuing until the first process's lock request is removed. However, if the second process does not issue an <MvLOCKFILE> before attempting to access the file in question, there is nothing to prevent this access from taking place. For this reason, we refer to the action of <MvLOCKFILE> as a lock request, rather than a lock: it is a request to other Miva Script processes that they 'respect' the current process's request for exclusive access. Another way of looking at this is: <MvLOCKFILE> does not interact with any other Miva Script tag, only other <MvLOCKFILE>s.

Even though <MvLOCKFILE> does not provide absolute security against a file being accessed by another process, it is still a useful technique: if you code an application properly <MvLOCKFILE> will provide protection against other instances of the same application accessing a file simultaneously.

Typically you would not have a lock request in place during the entire execution of your program, as this might unnecessarily block other processes from accessing the file in question. You need to identify blocks of code during whose execution it would be desirable to have a lock request in place. Then you surround the blocks of code with <MvLOCKFILE> and </MvLOCKFILE> tags. For example:

<MvLOCKFILE FILE="workers.txt">
   <MvEXPORT FILE="workers.txt" ...>
</MvLOCKFILE>

In this example, the program requests a lock on the text file workers.txt while an <MvEXPORT> is performed.

If a lock request exists on a file, and another process requests a lock on that file (with <MvLOCKFILE>), then the second lock request is queued until the first lock request is removed. When the lock request is removed, the second process's lock request becomes the 'primary' one. While the second process is waiting, its execution stops; if the process has to wait a long time, this could cause it to time out. Several lock requests can be queued at once.


Note: <MvLOCKFILE> uses a 'semaphore file' mechanism. The name of the semaphore file is not guaranteed be the same when Miva Script is used on different platforms, or to remain the same in future implementations of Miva Script. For this reason, programs should not be written in a way that relies on the existence of a specific semaphore file name.

Nesting <MvLOCKFILE>s

You can nest two more <MvLOCKFILE>...</MvLOCKFILE> tag pairs if you need to lock two different files at the same time. For example:

<MvLOCKFILE FILE="input.dat">
<MvLOCKFILE FILE="output.dat">
   ...
</MvLOCKFILE>
</MvLOCKFILE>

The <MvLOCKFILE> and </MvLOCKFILE> tags are paired: the innermost </MvLOCKFILE> removes the lock request created by the innermost <MvLOCKFILE> (in this example, the lock request on output.dat).

You should take care to avoid the situation known as a "deadly embrace": this occurs when a process makes a lock request on a file for which the same process already has a lock request in place. For example:

<MvLOCKFILE FILE="foo.dat">
...
   <MvLOCKFILE FILE="foo.dat">
   ...
   </MvLOCKFILE>
...
</MvLOCKFILE>

When the second <MvLOCKFILE> is executed, the process stops executing while it waits for the lock request on foo.dat to be removed; however, this will never happen, precisely because the current process, which is also the process that issued the first lock request, is now stopped! The process will wait until it eventually times out.


Note: If a process terminates without explicitly removing a lock request on a file, it will be removed automatically the next time an <MvLOCKFILE> requests a lock on the same file.

Database locking

As indicated in the database sections below, when you are adding (<MvADD>) or updating (<MvUPDATE>) a database record, Miva automatically prevents the same record from being updated by more than one process at the same time. This is true locking: no other Miva Script process can access a record while it is locked. <MvLOCKFILE> is neither required nor desirable in order to provide record-level locking. Other database operations--<MvPACK>, <MvMAKEINDEX>, and <MvREINDEX>--do not perform automatic locking. You may wish to use <MvLOCKFILE> to provide these tags with added data integrity (you should lock both the database and index files in question); however, since each of these tags tend to be time-consuming, we recommend that you use them 'off-line', thereby avoiding both data integrity issues and excessive resource consumption on your server.



Communicating with an HTTP server - MvCALL


Summary

<MvCALL 
    ACTION = "URL" 
    METHOD = "POST|GET" 
    FIELDS = "var1,var2,var3,..."
    FILES = "fvar1,fvar2,fvar3,...">
...code... 
<MvCALLSTOP>
...code...
</MvCALL> 

<MvCALL> uses the HTTP/1.0 protocol to emulate a browser and contact a remote host. Static HTML, XML, and SGML pages can be requested from the remote server, and data can also be sent to the remote server.

ACTION specifies the URL to be contacted: this must be a full URL starting with http. METHOD can be POST (send data) or GET (send data or request a document). FIELDS contains variables whose values are sent to the URL when POST is used as the method. FILES contains variables whose values are filenames that will be uploaded to the specified URL (see the section File uploading).

When <MvCALL> receives a document, it iterates once for each object (text or tag) that it receives. Several special variables (described below) are created with data about the object.

The <MvCALL>...</MvCALL> loop terminates when the entire document has been received, or when an <MvCALLSTOP> (optional) is encountered. <MvCALLSTOP> is an empty tag.



Note: <MvCALL> does not support the HTTPS (secure HTTP) protocol.



Requesting a document with <MvCALL>

To request a document using <MvCALL>, set the ACTION attribute to the URL of the document that you want to obtain, and set the METHOD attribute to 'GET'. The FIELDS attribute is not required. For example:

<MvCALL
    ACTION="http://www.your_server.com/company/contact.html"
    METHOD="GET">

<MvCALL> retrieves the document as a sequence of tags and text blocks. <MvCALL> passes the document to the program one object at a time; the <MvCALL>...</MvCALL> loop is executed once for each object. Consider the following HTML code:

<P>When in <B>Rome</B> do as the <I>Romans</I> do.</p>

<MvCALL> will split this code into 11 objects:

  1. <P>
  2. When in
  3. <B>
  4. Rome
  5. </B>
  6. do as the
  7. <I>
  8. Romans
  9. </I>
  10. do.
  11. </P>

Each time <MvCALL> returns an object, it sets the value of the special variable callobjecttype. callobjecttype has the value 'tag' if the object is a start- or end-tag, and 'text' if the object consists of text. Your program can use this value inside the <MvCALL> loop to decide how to process the object. The variable callvalue contains the text or tag object itself.

Tags

The variable callobjecttype has the value 'tag' if the object returned by <MvCALL> is a start- or end-tag. If the tag is a start-tag, <MvCALL> then further separates the tag into attribute/value pairs.

The following special variables are set for each tag:

  • callvalue: the whole tag, including angle brackets, tag name, and any attributes and attribute values.
  • callobjectelement: the tag name, for example 'IMG' for an <IMG> tag, '/TABLE' for a </TABLE> tag.
  • callobjectnumattributes: the number of attributes specified for the tag.
  • callobjectattributeN: if attributes have been specified for the tag, variables callobjectattribute1, callobjectattribute2, ... will be created, containing the names of the attributes as they appear in sequence.
  • callobjectvalueN: if attributes have been specified for the tag, variables callobjectvalue1, callobjectvalue2, ... will be created, containing the values of the attributes as they appear in sequence.

Consider the following example:

<IMG SRC="donkeys.gif" ALT="The Donkey Farm" HEIGHT="100" WIDTH="200">

<MvCALL> will set the following variables for this 'tag' object:

Name Value
callvalue the entire tag
callobjectelement IMG
callobjectnumattributes 4
callobjectattribute1 SRC
callobjectvalue1 donkeys.gif
callobjectattribute2 ALT
callobjectvalue2 The Donkey Farm
callobjectattribute3 HEIGHT
callobjectvalue3 100
callobjectattribute4 WIDTH
callobjectvalue4 200

All values, string and numeric, are returned without quotes.

Sometimes an attribute will be specified without an attribute name, as in:

<TABLE BORDER>

In this case, the attribute name and the attribute value are considered to be the same. In this example, callobjectattribute1 and callobjectvalue1 will each have the value BORDER.


Note: All of these variables (except callvalue) will be set to null if the current object is of type 'text'.

Text

The variable callobjecttype has the value 'text' if the object returned by <MvCALL> consists of text.

The variable callvalue will contain the actual string of text and white space.


Note: Sequences of whitespace between tags (such as newlines, spaces, and tabs) are returned as text objects by MvCALL.

Headers

When it retrieves a document, <MvCALL> also receives HTTP header information sent by the server. This information is stored in the following variables:

  • callnumberofheaders: This variable contains the number of headers that have been retrieved. The number and content of headers depends on the server software that is serving the document. The value of callnumberofheaders will not change during the iterations of the <MvCALL> loop through a particular document.
  • callreturnheaderN: The headers received are placed into a separate variables callreturnheader1, callreturnheader2, and so forth. The values of these variables do not change during the iterations of the <MvCALL> loop through a particular document.

Here is an example of header data:

callnumberofheaders=
callreturnheader1=
callreturnheader2=
callreturnheader3=
callreturnheader4=
callreturnheader5=
callreturnheader6=
6
HTTP/1.0 200 OK
Date: Fri, 17 Jan 1997 01:23:24 GMT
Server: Apache/1.1b0a
Content-type: text/html
Content-length: 390
Last-modified: Thu, 16 Jan 1997 23:40:10 GMT

MvCALL examples

The following code will retrieve and display an HTML page:

<MvCALL ACTION="http://..." METHOD="GET">
    <MvEVAL EXPR="{callvalue}">
</MvCALL>

This will display the page as if its URL had been accessed directly by the browser, except that any relative links (to images, for example) will not be resolved. One workaround for this is to put a <BASE> tag in the page being accessed, giving its absolute URL. Another is to put the appropriate <BASE> tag in the output stream that Miva sends to the browser:

<MvASSIGN NAME="url" VALUE="http://...">
<MvCALL ACTION="&[url];" METHOD="GET">
    <MvIF EXPR="{callobjecttype EQ 'tag' AND toupper(callobjectelement) EQ '/HEAD'}">
         <MvEVAL EXPR="<BASE HREF='&[url];'>">
    </MvIF>
    <MvEVAL EXPR="{callvalue}">
</MvCALL>

To display the page in its 'raw' HTML form, use:

<PRE>
<MvCALL ACTION="http://..." METHOD="GET">
    <MvEVAL EXPR="{encodeentities(callvalue)}">
</MvCALL>
</PRE>

Here is another simple <MvCALL> example:

<MvCALL METHOD="GET" ACTION="http://...">
<MvIF EXPR="{ callobjecttype EQ 'tag' }">
    <p>Tag name: <MvEVAL EXPR="{callobjectelement}"></p>
    <p>Number of attributes: <MvEVAL EXPR="{callobjectnumattributes}"></p>
<MvELSE>
    <p>Text: ==<MvEVAL EXPR="{callvalue}">==</p>
</MvIF>
<HR>
</MvCALL>

This program gets objects from the specified URL. If the object is a tag, the tag's name and the number of attributes are displayed. If the object is text, the text is displayed.

The output from this program will look like this:

 ... 
Tag name: B
Number of attributes: 0

Tag name: FONT Number of attributes: 2
Text: ==An out-of -office experience==
Tag name: /FONT Number of attributes: 0
...

Limitations

<MvCALL> does not provide a true HTML, SGML, or XML parser, so it has some limitations :

  1. It does not check whether a document is valid or well-formed
  2. It does not resolve internal or external entities
  3. It treats XML namespace prefixes a part of the tag (most of the time this is not a problem)

Nevertheless, <MvCALL> is a useful tool for processing structured documents, particularly those that are valid and well-formed.


Note: <MvCALL> may produce unexpected results if the document being requested uses the double-quote character (") instead of the entity &quot; in literal text.



Submitting data with <MvCALL>

There are two ways to use <MvCALL> to submit data to a CGI program on a server. These are the GET and POST methods, and are used in the same way as with an HTML form. The receiving program must know which method is being used and be set up to process data accordingly.


Note: It is not possible in Miva Script to modify the default HTTP headers when sending a request to a server.

Using GET

To use the GET method to submit data with <MvCALL>, set the METHOD attribute to 'GET'. The data that is submitted must be appended to the URL specified by ACTION. It is separated from the URL by a '?' character, and consists of a sequence of 'name=value' pairs separated by ampersands, '&'. For example:

http://www.my_isp.com/cgi-bin/prog1?animal1=fish&animal2=dinosaur

If the data contains any special characters, such as space, '=', '~', '&', and '+', it must be URL-encoded. The submitted data will be available to the receiving program via the QUERY_STRING CGI environment variable.

Here is an example using GET:

<MvCALL
    ACTION="http://www.my_isp.com/cgi-bin/prog1?animal1=fish&animal2=dinosaur"
    METHOD="GET">
...
</MvCALL>

Using POST

If you use the POST method to submit data, the data is not sent to the server appended to the URL, but rather in the HTTP headers. The format in which the data is received is the same as when GET is used. That is, a sequence of 'name=value' pairs separated by ampersands, '&'.

Instead of obtaining the data from the QUERY_STRING variable, the program receiving the data must read it from its standard input.

To use this method, set the METHOD attribute of <MvCALL> to 'POST'. The FIELDS attribute should contain a list of variables whose names, together with their values, are sent to the URL specified with the ACTION attribute. Here is an example:

<MvASSIGN NAME="animal1" VALUE="fish">
<MvASSIGN NAME="animal2" VALUE="dinosaur">
<MvCALL
    ACTION="http://www.my_isp.com/cgi-bin/prog2"
    METHOD="POST"
    FIELDS="animal1,animal2">
...
</MvCALL>

Processing the response

Since CGI programs generally send a response back to the client that sent it data, the <MvCALL> loop can and should be prepared to deal with the document that is sent back. For example:

<MvCALL
    ACTION="http://www.my_isp.com/cgi-bin/prog1?animal1=fish&animal2=dinosaur"
    METHOD="GET">
      <MvEVAL EXPR="{callvalue}">
</MvCALL>

This very basic code simply displays the document that the CGI program sends back.



Calling Miva Script (and other) programs with <MvCALL>

You can use <MvCALL> to call another Miva Script program and pass data to it. To do this, use GET or POST as explained in the previous section, and specify a Miva Script program with the ACTION attribute.

For example:

<MvCOMMENT>Using GET</MvCOMMENT>
<MvCALL
    ACTION="http://www.my_isp.com/prog1.mv?animal1=fish&animal2=dinosaur"
    METHOD="GET">
...
</MvCALL>
<MvCOMMENT>Using POST</MvCOMMENT> 
<MvASSIGN NAME="animal1" VALUE="fish">
<MvASSIGN NAME="animal2" VALUE="dinosaur">
<MvCALL
    ACTION="http://www.my_isp.com/prog2.mv"
    METHOD="POST"
    FIELDS="animal1,animal2">
...
</MvCALL>

In the two examples above, the Miva Script program that is being called can access the variables animal1 and animal2 with the values provided.

<MvCALL> can also call any CGI program (such as a Perl script), using the same constructions; in the ACTION attribute, just substitute the URL of the CGI program being called.

With GET, you can also use the value list syntax for passing arguments to a program:

<MvCOMMENT>Using GET</MvCOMMENT>
<MvCALL
    ACTION="http://www.my_isp.com/prog3.mv?fish+dinosaur"
    METHOD="GET">
...
</MvCALL>

In this case, the Miva Script program that is being called can access the submitted values via the arg2 and arg3 variables.

Running system commands

Miva adheres to the principle of 'site sandboxing', which means that it will not interfere with the server or other users on the server. For this reason Miva does not allow system commands (such as UNIX shell commands) to be run directly from Miva. If you need to run system commands, you may be able to write a CGI program (using Perl, shell, or another language) that can execute system commands, and use <MvCALL> to call that CGI program.



File uploading

Miva supports two forms of file uploading: sending a file from one host to another, and receiving a file from an end-user.


Summary

Sending a file
<MvCALL ACTION="http://www.recipient.com/uploadscript.mv"
    METHOD="POST"
    FILES="var1,var2,...">

Send the files whose name are given by var1,var2,... (relative to the Miva data directory) to the host and script specified by ACTION, where the upload will be processed by the specified script.

Receiving a file
<FORM ACTION="http://www.recipient.com/uploadscript.mv"
    METHOD="POST"
    ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="some_name">
<INPUT TYPE="SUBMIT">
</FORM>

Enables an end-user to choose a file on the local network and send it to the host and script specified by ACTION, where the upload will be processed by the specified script.

Whether you are sending or receiving a file, if the recipient script is a Miva Script program, it must contain the following user-defined functions:

<MvFUNCTION NAME="Miva_ValidateFileUpload"
    PARAMETERS="field, filename, content_type">
<MvFUNCTION NAME="Miva_ProcessFileUpload"
    PARAMETERS="field,filename,status,tempfile,content_type,size">



Authenticating the upload

Files can be uploaded to any CGI script that supports file upload. For security reasons--in order to protect hosts that do not want to receive uploaded files from receiving them--if the script at the receiving end is a Miva Script program, it must contain two user-defined functions: one to validate the upload, and the other to process the uploaded file. If either of these scripts are missing, or have the wrong number of parameters, the upload will fail.

Validating the upload

The recipient script must contain the following function, which validates the upload:

<MvFUNCTION NAME="Miva_ValidateFileUpload"
    PARAMETERS="field,filename,content_type">
...
<MvFUNCRETURN VALUE="{return_code}">
...
</MvFUNCTION>

Note: The specific parameter names used here are arbitrary.

This function will be called automatically by the Miva processor (your program does not need an explicit call to this function). It will be called with three arguments, the name of the Miva variable (field) that contains the name of the uploaded file, the filename itself, and the MIME type (content_type) of the file on the uploading server. The actions of this function can be whatever you want, but the function must eventually return one of three values:

  • -1: skip this file--don't upload it at all.
  • 0 (zero): upload the whole file, no matter how long it is.
  • Any positive integer N: upload only the first N bytes of the file.

How you arrive at this return value is up to you: for example, if you want to upload all files in their entirety, the body of the function could consist of only:

<MvFUNCRETURN VALUE="0">

To skip GIF images but upload all other files:

<MvIF EXPR="{content_type EQ 'image/gif'}">
    <MvFUNCRETURN VALUE="-1">
<MvELSE>
    <MvFUNCRETURN VALUE="0">
</MvIF>

To upload only up to 1000 bytes of any file:

<MvFUNCRETURN VALUE="1000">

Processing the upload

The recipient script must contain the following function, which processes the upload:

<MvFUNCTION NAME="Miva_ProcessFileUpload"
    PARAMETERS="field,filename,status,tempfile,content_type,size">
...
</MvFUNCTION>

Note: The specific parameter names used here are arbitrary.

This function will be called by the Miva processor with certain information about the file being uploaded (your program does not need an explicit call to this function):

  • The Miva variable (field) containing the filename.
  • The filename itself.
  • The upload status: this will be one of the strings 'SKIPPED', 'COMPLETE', or 'PARTIAL', and indicates whether the file was skipped (not uploaded at all), uploaded completely, or partially uploaded.
  • A temporary file tempfile in which the uploaded file will be stored. This file will be deleted as soon as Miva_ProcessFileUpload() terminates.
  • The MIME type (content_type) of the file, on the uploading server.
  • The size of the original file (untruncated)

The actions of this function can be whatever you want, but probably the most important thing you would do in this function would be to make a copy of the temporary file (tempfile), since it will be deleted when the function terminates. For example:

<MvASSIGN NAME="save_it" VALUE="{frename(tempfile,permfile)}">


Sending files

To send a file to another server, use an <MvCALL> in the following manner:

<MvASSIGN NAME="f1" VALUE="file1.txt">
<MvASSIGN NAME="f2" VALUE="file2.htm">
<MvCALL ACTION="http://www.recipient.com/uploadscript.mv"
    METHOD="POST"
    FILES="f1,f2">

The FILES attribute specifies a list of one or more variables whose values are the names of files to be uploaded; these filenames must be relative to your Miva data directory. The METHOD must be POST (or PUT, which will generally be implemented as POST). The files are uploaded to the server specified by ACTION, but the results of the upload process depend on how the specified script (uploadscript.mv in this example) processes the data it receives. If this script is a Miva Script program, it must contain definitions for the two functions Miva_ValidateFileUpload() and Miva_ProcessFileUpload(), described above; both of these functions will be called once for each uploaded file, and can skip the file, upload it in its entirety, or upload it partially, and then can save the file, allow it to be deleted without saving, and perform any other desired processing.


Note: For this kind of upload, the field parameter (the first argument passed to Miva_ValidateFileUpload() and Miva_ProcessFileUpload()) is the name of the variable containing the filename.


Note: You can also send data to the recipient script using the FIELDS attribute of <MvCALL> at the same time that you send the file(s) to be uploaded.



Receiving files from end-users

To enable users of your script to send files to your server (or another server), use an HTML FORM in the following manner:

<FORM ACTION="http://www.recipient.com/uploadscript.mv"
    METHOD="POST"
    ENCTYPE="multipart/form-data">
<INPUT TYPE="FILE" NAME="some_name">
<INPUT TYPE="SUBMIT">
</FORM>

The METHOD must be POST (or PUT, which will generally be implemented as POST), and the ENCTYPE attribute must be set to 'multipart/form-data'. The <INPUT TYPE="FILE"> element creates a form object such as the following:

The user browsing a page containing this object can use it to enter or choose a file on the local network. When the form is submitted, the file is uploaded to the server specified by the ACTION, but the results of the upload process depend on how the specified script (uploadscript.mv in this example) processes the data it receives. This script must contain definitions for the two functions Miva_ValidateFileUpload() and Miva_ProcessFileUpload(), described above; both of these functions will be called once for each uploaded file, and can skip the file, upload it in its entirety, or upload it partially, and then can save the file, allow it to be deleted without saving, and perform any other desired processing.


Note: For this kind of upload, the field parameter (the first argument passed to Miva_ValidateFileUpload() and Miva_ProcessFileUpload()) is the NAME of the <INPUT> object used to specify the file.


Note: In general, browsers do not permit files specified with http://-style URLs to be uploaded using this method. Instead, local network drive paths (such as c:\...) should be specified. Some browsers do not allow a default VALUE for <INPUT TYPE="FILE"> fields.



Arrays

An array is a data structure in which many values are associated with the same variable name, but each value has its own 'subscript' or 'index value' to distinguish it from the others. For example, a series of names could be stored in variables name_1, name_2, name_3, and so forth. In this case we could say that the names are stored in an array called names. Data stored in this way is much easier for a program to manage than data stored in completely unrelated variable names.

Miva Script does not have built-in support for arrays, but it is easy to simulate arrays by using macros. The array subscripts can be generated by a value referred to by a macro.

Here is an example that reads a list of values from a data file and stores each one in a different element of an array:

<MvASSIGN name="counter" value="1">
<MvIMPORT 
    FILE="workers.dat" 
    FIELDS="worker,amount" 
    DELIMITER="|">
<MvASSIGN name="person_&[counter];" value="{worker}">
<MvASSIGN name="salary_&[counter];" value="{amount}">
<MvASSIGN name="counter" value="{counter+1}">
</MvIMPORT>

This example actually uses two arrays, one whose variables start with person_, and another whose variables start with salary_. When a line of data is read from the data file, values are stored in the variables worker and amount. The first time a line of data is read, the value of the variable counter is 1. Now remember that macros are evaluated before a line of code is executed, so the first two <MvASSIGN> tags after the <MvIMPORT> effectively become:

<MvASSIGN name="person_1" value="{worker}">
<MvASSIGN name="salary_1" value="{amount}">

Then the variables person_1 and salary_1 are assigned the current values of worker and amount, respectively. Next, the value of counter is increased by one. Therefore, the next time through the loop, the variables person_2 and salary_2 are created and assigned values. When all of the data has been read in, two series of variables person_1, person_2,...,person_N and salary_1, salary_2,...,salary_N have been created and assigned values.

One advantage of storing data in this way is that elsewhere in the program it would be very easy to implement a loop that reads through the entire array; another is that if you know the array subscript associated with a particular person, it is easy to access both the name and salary values for that person.



Sending and receiving email

Miva Script provides tags that can send and receive email. SMTP is used to send mail from any valid mail server, and Miva Script documents can receive email by becoming POP3 clients.



Sending mail - <MvSMTP>


Summary

<MvSMTP
    TO="to_address1,to_address2,..." 
    SUBJECT="expression" 
    CC="cc_address1,cc_address2,..." 
    MAILHOST="mailhost" 
    FROM="from_address">
    [optional headers]
    [blank line]
    ... message body (text/tags)...
</MvSMTP>

Send mail, using SMTP, to the to_address1, to_address2, ... email addresses. Required are: at least one TO value, a MAILHOST value consisting of the domain name or IP address of a host that understands SMTP, and the sender's email address (FROM). Optionally, one or more CC ("carbon copy") addresses and a SUBJECT line can be specified. The message body appears between the <MvSMTP> and </MvSMTP> tags.


Here is an example of sending email with <MvSMTP>:

<MvSMTP
    TO="les@some_isp.com,janice@some_isp.com"
    FROM="royc@my_isp.com"
    MAILHOST="my_isp.com"
    CC="leanne@some_isp.com,toyah@some_isp.com"
    SUBJECT="Weatherfield Alien Abduction Society">

Friends: the next meeting of the Society will be
on April 16, 1998.
</MvSMTP>

A value for TO is mandatory and indicates that the mail is outgoing. TO can contain a single email address or a comma-separated list of email addresses.

FROM is mandatory and contains the email address of the person sending the message.

MAILHOST is mandatory and must contain the domain name or IP address of your mail host (a host that understands SMTP). If you don't know this value, check with your ISP or system administrator.

SUBJECT is optional and can contain text that will be used as the message's Subject line. If no subject is specified, then the text 'no subject' is is used as the default subject.

CC is optional and can contain a single email address or a comma-separated list of email addresses.

Mail headers, if present, must be placed on the line directly after the <MvSMTP> tag. Whether or not there are mail headers, the line before the message contents must be left blank.

Enter the text of your message between the blank line and the </MvSMTP> tag. The message can also be generated by other Miva Script tags between the <MvSMTP> and </MvSMTP> tags; for example, an <MvCALL> tag could be used to obtain a text file.

Address formats

Most SMTP hosts are very flexible as to the formats the allow for e-mail addresses (as specified with FROM, TO, and CC). For example, you may wish to specify "real names":

<MvSMTP TO="Roy Cropper <royc@my_isp.com>" ...>

If you include a name with the address, surround the address with '<' and '>', as in the example. You must not put a comma in the name.

Some servers do not allow this form of address, so before you "go live" with a script you should test this format on the server you intend to use. By contrast, some SMTP servers require that addresses be surrounded by '<' and '>'. In all cases, you should perform tests with your server, and ensure that the values you provide for FROM, TO, and CC meet your server's requirements.

Mail headers

In addition to ordinary content, mail messages can contain mail headers: for example, Reply-To, Return-Path, and Sender. Mail headers, if present, must be placed on the line directly after the <MvSMTP> tag. Whether or not there are mail headers, the line before the message contents must be left blank. For example:

<MvSMTP
    TO="les@some_isp.com,janice@some_isp.com"
    FROM="royc@my_isp.com"
    MAILHOST="my_isp.com"
    CC="leanne@some_isp.com,toyah@some_isp.com"
    SUBJECT="Weatherfield Alien Abduction Society">
Reply-To: roy.cropper@my_isp.co.uk

Friends: the next meeting of the Society will be
on April 16, 1998.
</MvSMTP>

This adds the Reply-To header to the message.

To understand how <MvSMTP> handles headers, you need to understand something about how mail messages are handled generally. The basic situation is that a user uses a program called a mail client to create a mail message, and send it to another program called a mail server, which communicates with the network. Because there are many different implementations of mail clients and mail servers, the specific tasks that each one carries out will differ from program to program. In particular, some mail clients and some mail servers will interpret various mail headers, and some won't. Miva, acting as a mail client when it encounters an <MvSMTP> tag, interprets only the data passed to it with the TO, FROM, CC, and SUBJECT attributes, and generates the appropriate headers and SMTP codes for this data. Any other headers are passed to the mail server unchanged. These then become the responsibility of the server; how they are processed (if at all) depends on the server configuration. For this reason, headers such as Bcc, which require extra processing, are not guaranteed to have the expected effect. You should test with the specified MAILHOST any such headers that you intend to use.

Some useful references are: RFC 822 (http://www.sendmail.org/rfc/0822.html), the Standard for Internet Text Messages, for more information on mail headers; RFC 821 (http://www.sendmail.org/rfc/0821.html) for information on SMTP; and RFC 1123 (http://www.sendmail.org/rfc/1123.html) for information on Internet hosting in general.



Reading mail - MvPOP


Summary

<MvPOP
    MAILHOST="host"
    LOGIN="login_name"
    PASSWORD="password"
    DIRECTORY="directory">
    ...code...
    <MvPOPDELETE>
    ...code...
    <MvPOPSTOP>
    ...code...
</MvPOP>

<MvPOP> creates a loop that logs into the POP3 mail server named host, using the login_name and password login information, and retrieves all incoming mail messages, one message at a time. Messages are stored on your local system (that is, the system running Miva, not necessarily the system running the POP3 server) under unique filenames, in the directory specified with DIRECTORY. The host, login name, password, and storage directory are all required. When a message is retrieved, information about the message is stored in the special variables messagebody, messagesubject, messagereplyto, messagesender, and messagedate. This information can then be processed inside the loop. If an <MvPOPDELETE> (optional) is executed, the current message is deleted from the server. If an <MvPOPSTOP> (optional) is executed, the <MvPOP> loop terminates. <MvPOPDELETE> and <MvPOPSTOP> are empty tags.


Here is an example of reading mail with <MvPOP>:

<MvPOP
   MAILHOST="mail.my_isp.com"
   LOGIN="my_userid"
   PASSWORD="pa55w0rd"
   DIRECTORY="mymail">
<p>You received mail from: <MvEVAL EXPR="{messagesender}">.<br>
This mail is about: <MvEVAL EXPR="{messagesubject}">.<br>
This mail was sent on: <MvEVAL EXPR="{messagedate}">.<br>
I stored this mail in the file: <MvEVAL EXPR="{messagebody}">.<br>
You can reply to: <MvEVAL EXPR="{messagereplyto}">.
</p>
</MvPOP>

This code reads incoming email messages belonging to the user my_userid, who has the password pa55w0rd, on the mail server mail.my_isp.com; these messages are stored in a directory called mymail.

Note: The storage directory should be specified as a directory relative to the data directory of the owner of the active document if Miva Empresa is running in server-safe (multi-user) mode; it can be a relative or absolute path if Miva Empresa is running in standard mode.

When <MvPOP> retrieves an email message, it stores information about the message in several special variables:

  • messagesubject: the 'Subject:' line of the incoming message.
  • messagedate: the 'Date:' line of the message.
  • messagesender: the 'From:' line (sender) of the message. Do not use this value for replying to a message; instead, use the value of messagereplyto.
  • messagereplyto: the 'Reply-To:' line (the address to which replies should be sent). If the incoming message does not have 'Reply-To' in its header, then the 'From:' line is used for this value.
  • messagebody: a guaranteed unique file name in which the message body has been saved.

Some or all of the first four values in the list may be unavailable if the message header is missing or truncated.

Some sample output would be:

You received mail from: Walter Mitty. 
This mail is about: My latest adventures. 
This mail was received on: Tue, 10 Mar 1998 13:50:10 -0500 (EST). 
I stored this mail in the file: testmail/MIVA_POP.a06431. 
You can reply to: Walter Mitty. 

If there is more than one incoming message, <MvPOP> will iterate through them and print these fields for each message.

Deleting an email message

By default, <MvPOP> does not remove email messages from the POP3 server. If you want to remove a message, you need to execute an <MvPOPDELETE> tag inside the <MvPOP> loop. This deletes the current email message on the server. The message will still be processed by the application in this iteration of the <MvPOP> loop.



Using databases

Miva Script supports two kinds of databases: xbase3 (dBaseIII compatible) databases, which are supported on all platforms, and ODBC databases which are supported only on Microsoft Windows.

Miva Script has tags that let you perform database operations, such as: creating, opening, and closing a database; adding, deleting, and updating records; searching for records; and creating and using database indexes. Any database files or database indexes that you use must be located in your data directory.

The full Miva database feature set is currently supported only for xbase3 databases.

Access to ODBC datasources is currently supported only with Miva Mia.


Tip: If possible, use the Miva Mia to perform time-consuming database operations such as converting a flat file to a database, and creating a new index for a large database. This can avoid heavy consumption of web server resources, and having your program time out. You can set Miva Mia's timeout value to be as high as you like.



Using xbase3 databases

With the exception of <MvOPEN>, <MvSKIP> and <MvGO>, the Miva Script database tags described below are supported only for xbase3 databases. The optional attribute VIEW, available with several database tags, is currently supported only when accessing ODBC databases with <MvSKIP> and <MvGO>.



Introduction to databases

Miva xbase3 databases organize data into a simple tabular format that consists of records (which you can think of as the rows of the table) that each have one or (usually) more fields (which you can think of as the columns of the table). Note that when we refer to a 'table' here, we don't mean an HTML table (although it is very easy to extract data from a database an put it in such a table). Here 'table' refers to the ordering of the data--the way that the Miva Script language enables you to manipulate the data.

Here is a small example:

Benson President 250000 19930630 1 Former V-P sales
Rochester V-P Finance 120000 19921015 1 Weird ties
Jeeves V-P Technology 150000 19880710 1 Funny hats
Hudson V-P Sales 130000 19971201 1  

This example is from an employee database. Each employee is represented by a record (a row), that has fields for data such as name, title, salary, start date, status, and comments.

Summary of database operations

Databases are flexible enough that it is not really possible to give a simple set of steps for using them. However, here are some database operations that apply to almost every database.

  1. Creating the database. A database must be created before it can be used. When you create a database, you are just defining its structure: a database file is created on your system, but it doesn't contain any data yet. This step needs to be done only once for each database. Miva Script uses the <MvCREATE> tag to create a database.
  2. Database aliases. An important aspect of working with Miva databases that you need to understand is that databases are almost always referred to in Miva Script tags using a database alias, rather than the actual physical filename of the database. An alias is simply a name that you assign to the database when you open it. The same database file can be open two or more times simultaneously with different aliases. If the database has only one current alias, then the alias and the database can be thought of as identical; sometimes this manual will, for convenience, and if the intention is clear, use the term 'database' when strictly speaking 'database alias' is correct.
  3. Opening the database. A database must be open before you can use it. When a database is created, it is opened automatically. Usually, however, the script that you use to create the database will be run only once, so if you use other scripts to manipulate the database, you have to explicitly open the database, using the <MvOPEN> tag. A database stays open until the script terminates, or until you close it explicitly with <MvCLOSE>. The next time you use the database, you have to open it again. If you want, you can use a different alias for the database the next time you open it.
  4. The primary database. Miva Script allows several database aliases to be open at the same time, and each of them can be accessed in Miva Script tags by referring to the alias. As a convenience, Miva Script allows one alias at a time to be designated the 'primary' database alias. This means that if a database tag does not explicitly name an alias, the primary alias is implied. An alias becomes the primary alias automatically when it is created or opened (subsequent create or open operations override this); an alias can also be made the primary alias explicitly using <MvPRIMARY>.
  5. Adding and updating records. Often you will want to add records or change one or more fields in an existing record. The way you do this in Miva Script is to assign values to a special set of variables corresponding to the database fields, and then use the <MvADD> tag to add a record, or <MvUPDATE> to update the current record.
  6. Indexing the database. When records are added to a database, they are simply added at the bottom of the 'logical' table structure. Unless you explicitly added them in a specific order, the records will not be ordered in any way. Often you will want to be able to group or order records according to the values of certain fields (for example, sort names alphabetically, or group together all employees with the same job title). For this you can create an index: instead of physically reordering the records, a database index changes the way the order appears to various Miva Script database commands.
  7. Database navigation. There are several ways of moving from one record to another. You can find (<MvFIND>) the first record in the database index that contains a specific value; you can go (<MvGO>) to a specific record in physical order; and you can skip (<MvSKIP>) to the next record in physical or indexed order.
  8. Filtering the database. You can use the <MvFILTER> tag to make 'visible' only those records that satisfy a specified criterion.
  9. Reading and displaying database records. In order to read and display the contents of a record, you first navigate to the record. At that point the contents of each of the record's fields will be available in a set of special database variables.
  10. Deleting records. To delete a record, you first navigate to the record. The <MvDELETE> tag marks the record for deletion; the <MvPACK> tag physically removes it.
  11. Obtaining a database's structure: the <MvREVEALSTRUCTURE> tag is used to find out information about the kinds of fields in a database record for a specific database.


Creating a database - MvCREATE


Summary

<MvCREATE
    NAME = "db_alias"
    DATABASE = "db_file"
    FIELDS = "fieldname1 CHAR(max_chars), 
              fieldname2 NUMBER(digits_before.digits_after), 
              fieldname3 DATE, 
              fieldname4 BOOL,
              fieldname5 MEMO
              ..."
    TYPE="xbase3"> 

<MvCREATE> creates a new database, with filename db_file, having the data structure specified by FIELDS. The NAME, DATABASE, and FIELDS attributes are all required. Fields in the database can be of type CHAR, NUMBER, DATE, BOOL, and MEMO. For CHAR fields, the maximum number of characters must be specified; for NUMBER fields, the numbers of digits allowed before and after the decimal point can be specified. If db_file already exists, it will be overwritten with an empty database with the specified structure. When a database is created it is implicitly opened (just as with <MvOPEN>) with the alias db_alias. db_alias cannot contain the '.' (period) character. <MvCREATE> is an empty tag.



Note: The TYPE attribute of <MvCREATE> has the default value xbase3. This attribute can be omitted, since <MvCREATE> is currently supported only for xbase3 databases.

A database must be created before it can be used. When you create a database, you are just defining its structure; a database file is created on your system, but it doesn't contain any data yet. This step needs to be done only once for each database.

Example database

Here is an example that creates a database; this database will be used in other examples below.

<MvCREATE
    NAME="worker_db"
    DATABASE="hr/dbs/workers.dbf"
    FIELDS="employee CHAR(40),
            title CHAR(20),
            salary NUMBER(7.2),
            started DATE,
            fulltime BOOL,
            comments MEMO">

Identifying the database

The NAME attribute specifies an alias for the database. An alias is simply a name that you assign to the database when you open it. Databases are almost always referred to in Miva Script tags using a database alias, rather than the actual physical filename of the database. The same database file can be open twice or more simultaneously with different aliases. The alias in the example above is 'worker_db'. If you want, you can use a different alias for the database the next time you open it.

The DATABASE attribute contains the actual filename of the new database to be created, possibly prepended with a directory path. This location is relative to the data directory for the current user. If you are creating the database in a subdirectory of the data directory, you must create the subdirectory first, using the fmkdir built-in function or explicitly through your operating system. You can use any file extension for the database file, but .dbf is recommended. The database file in the example above is hr/dbs/workers.dbf.

Defining database fields

The FIELDS attribute contains a comma-separated list of field names with their data types and parameters. Database field names can be up to 10 characters in length.

Fields can be assigned one of five different data types: CHAR (character), NUMBER, DATE, BOOL (boolean/logical), and MEMO (dynamic length).

  • CHAR field definitions must specify the maximum length of the data in that field. For example, 'CHAR(40)' specifies a maximum length of 40 characters. The maximum length of a character field is 254 characters.
  • NUMBER field definitions can specify the number of digits allowed in the number. This specification can have two formats: 'NUMBER(N)' indicates that the number can have N digits, but cannot contain a decimal point; 'NUMBER(N.M)' indicates that the number can have N digits before the decimal point and M digits after the decimal point. For example, 'NUMBER(7.2)' specifies 7 digits before the decimal point and 2 digits after. If no digit specification is provided, then NUMBER(19) is assumed.

    Numeric values with more than the specified number of decimal places will be truncated. Numeric values with more than the specified number of digits before the decimal place will not be entered in the database.

  • DATE fields have a fixed length. Dates have the form YYYYMMDD.
  • BOOL fields have a fixed length. These fields have the value '0' (false) or '1' (true).
  • MEMO fields have a dynamic length. MEMO data is stored in a separate file that has the same name as the database file, but with the .dbt file extension. All of the MEMO fields for all of the records in the database are stored in this file, but they are accessed individually, just like any other type of field. You should not delete this file.

The example above creates five fields: two CHAR fields named employee and title with maximum lengths of 40 and 20 characters, respectively; a NUMBER field called salary whose values can have 7 digits before the decimal and 2 after; a DATE field called started; a BOOL field called fulltime; and a MEMO field called comments.



Specifying the primary database alias - MvPRIMARY


Summary

<MvPRIMARY NAME="db_alias">

Several database aliases can be open at the same time, but one alias is said to be the primary alias. This is the default alias for all database operations (except creating and opening a database). If the NAME attribute is not specified by a database tag (except <MvCREATE> and <MvOPEN>), that tag's action will be performed on the database pointed to by the primary alias. By default, the primary alias is the last open to be opened with <MvOPEN> or <MvCREATE>. If more than one alias is open, <MvPRIMARY> makes the alias db_alias the primary one (this value is required). db_alias must currently be open. <MvPRIMARY> is an empty tag.


Note: For convenience, this manual will refer to "the database pointed to by the primary alias" as "the primary database".



Accessing fields in a record

When you are working with a database, Miva Script maintains the notion of a current record, or of a record pointer that points at the current record. A record can become the current record because you've just created it with <MvADD>, or because you've navigated to it using <MvFIND>, <MvGO>, or <MvSKIP>.

Miva Script creates variables that correspond to the fields of the current record in each open database, and it is through these variables that your program can read and write the data in the record.

For each database field of type CHAR, NUMBER, BOOL, or MEMO, a variable named db_alias.d.fieldname is created, where db_alias is the name under which the database is aliased, and fieldname is the field name. When a particular record is the current record, the variable db_alias.d.fieldname automatically contains the value of that record's fieldname field. For example, if the database worker_db has a field called title, then the following expression will display the current record's title field:

<MvEVAL EXPR="{worker_db.d.title}">

For each field fieldname of type DATE, special variables are created with the following values:

  • fieldname_day: Contains the day of the month (as a two-digit number)
  • fieldname_month: Contains the month of the year (as a two-digit number)
  • fieldname_year: Contains the year (as a four-digit number)
  • fieldname_raw: contains an exact date in yyyymmdd string format

Suppose the database with alias worker_db has a DATE field called started, and the date stored in the started field for the current record is April 16, 1998.

<P>Date: <MvEVAL EXPR="{worker_db.d.started_day$'/'$worker_db.d.started_month$'/'$worker_db.started_year}"></P>
<P>Raw date: <MvEVAL EXPR="{worker_db.d.started_raw}"></p>

This code will display:

Date: 16/04/1998
Raw date: 19980416

See the section Adding a record to a database for more examples of using database records.


Note: Database variables are automatically right-trimmed. The length of the contents of a database variable is equal to the actual length of the contents assigned to the corresponding field, minus any extra spaces on the right. For example:
<MvASSIGN NAME="db.d.name" VALUE=" cow ">
<MvADD>
<MvEVAL EXPR="{len(db.d.name}">
This gives the result '4'.

In general, the length of a database variable is not equal to the length of the corresponding database field, as defined with <MvCREATE>.


If you are referring to fields of the primary database, you can use d.fieldname instead of the longer form db_alias.d.fieldname. If the primary database does not have a field called fieldname, then the value of d.fieldname will come from the open database that does have a field fieldname and was most recently the primary database (a database becomes the primary database when it is created, opened, or made primary using <MvPRIMARY>). If no open database has the field fieldname, then d.fieldname is undefined. If the variables s.fieldname and l.fieldname do not exist, then the variable fieldname (that is, without a prefix) is equivalent to d.fieldname.


Tip: Avoid this common error: because of Miva Script's variable scope rules, if the variable vname exists and the primary database db_alias has a field called vname, the value of the variable vname will be interpreted as the value of db_alias.d.vname. Consider the following assignment of a value that will be stored in the database:
<MvASSIGN NAME="db_alias.d.title" VALUE="{title}">
This will have the effect of assigning db_alias.d.title its current value, because title is interpreted as db_alias.d.title. To avoid this, do one of the following:
  • Specify the global variable:
    <MvASSIGN NAME="db_alias.d.title" VALUE="{g.title}">
    ( g.title must be specified both when it is assigned a value itself, and when its value is assigned to the database variable db_alias.d.title)
  • Use a different variable name:
    <MvASSIGN NAME="db_alias.d.title" VALUE="{f_title}">

The same variables that you use to display the fields of the current record are also used when you want to update a record with <MvUPDATE> or add a record with <MvADD>.

Values for the special variables db_alias.d.totrec (total number of records in the database with alias db_alias) and db_alias.d.recno (current physical record number in the database with alias db_alias) are also set automatically when a database is open. The boolean variable db_alias.d.eof is true if the end of the database with alias db_alias has been reached. When referring to the primary database, d.totrec, d.recno, and d.eof; totrec,recno, and eof (without the prefix) are equivalent to the d. variables if no s. (system) or l. (local) variable with the same name exists.


Note: The physical record number of a record may change if the database is packed (that is, some records are physically deleted). For this reason, you should not code in a way that uses the physical record number as a unique identifier by which a record can be referred to. You may instead wish to define a database field in which you store a unique identifier for each record.



Opening and closing databases

The <MvOPEN> and <MvCLOSE> tags are used to open and close databases.

Opening a database - MvOPEN


Summary

<MvOPEN
    NAME="db_alias"
    DATABASE="db_file"
    TYPE="xbase3|odbc"
    INDEXES="index1.mvx,index2.mvx,...">

Opens the database with filename db_file, under the alias db_alias, and makes it the primary database. The record pointer is positioned at the first record. NAME and DATABASE are required. If indexes have been created for the database, they can optionally be loaded when the database is opened. TYPE is optional for xbase3 databases; it must have the value 'odbc' if you are opening an ODBC database. <MvOPEN> is an empty tag.


A database must be open before you can use it. When a database is created, it is opened automatically. Usually, however, the script that you used to create the database will be run only once, so if you use other scripts to manipulate the database, you have to explicitly open the database, using the <MvOPEN> tag. A database stays open until the script terminates, or until you close it explicitly with <MvCLOSE>. The next time you use the database, you have to open it again. If the current script makes a call to another script (or even the same script) the database will have to be opened again if operations are to be performed on it.

Here is an example:

<MvOPEN
    NAME="worker_db"
    DATABASE="hr/dbs/workers.dbf"
    INDEXES="idx1.mvx,idx2.mvx">

When you open a database it becomes the primary database for the program regardless of how many other databases were previously opened or the order in which they were opened. A database becomes the primary database by default if it is the one most recently opened. However, you can specify a new primary database with <MvPRIMARY>.

NAME and DATABASE are required. The alias specified by NAME doesn't have to be the same one used when the database was created. You can also have the database open multiple times simultaneously under different aliases, and update it under any of these aliases.

The optional INDEXES attribute contains a comma-separated list of index files that will be associated with the database being opened. All indexes are updated records are added or updated, or the database is packed. The first index file in the list will become the main index; it governs the way that the database will be sorted for navigation.

If the program will be modifying the database that is being opened, all relevant index files should be loaded so that they will be properly updated (you can also update the indexes at a later time using <MvREINDEX>). If a database is being opened only for reading information, an index is not necessary (unless you are using <MvFIND>), although using one will speed up access.

Closing a database alias - MvCLOSE


Summary

<MvCLOSE NAME="db_alias">

<MvCLOSE> makes the database alias db_alias unavailable; the database file may still be open under a different alias. If NAME is not specified, the primary database (the one most recently opened with with <MvCREATE> or <MvOPEN>) is closed. <MvCLOSE> is an empty tag.

All open databases are closed automatically when a Miva Script program finishes executing, however, you may wish to close an database explicitly when you've finished with it, so as to prevent the database being used accidentally.




Adding, updating, and deleting records

The following sections explain how to modify the data in a database. Database and index files are locked at the record level automatically when records are added or updated to prevent data corruption (that is, two programs cannot update the same record at the same time). Entire files can be locked using <MvLOCKFILE>.

Adding a record to a database - MvADD


<MvADD NAME="db_alias">

<MvADD> adds a record to the end of the database that has alias db_alias and positions the record pointer at that record. If NAME is omitted, the record is added to the primary database. Before <MvADD> is executed, the information to be stored in each field that you want to insert in the record must be assigned to variables (described below) corresponding to each field. <MvADD> locks the record that is being added. <MvADD> is an empty tag.


Here is an example that adds a record to a database:

<MvOPEN 
    NAME="worker_db"
    DATABASE="hr/dbs/workers.dbf"
    INDEXES="idx1.mvx,idx2.mvx">
<MvASSIGN NAME="worker_db.d.employee" VALUE="Walter Skinner">
<MvASSIGN NAME="worker_db.d.title" VALUE="Assistant Director">
<MvASSIGN NAME="worker_db.d.salary" VALUE="150000">
<MvADD>

Instead of explicitly assigning values to database fields, <MvADD> obtains values implicitly, from variables whose names are the same as the field names (except possibly for prefixes).

In this example, the database has fields called employee, title, and salary (also started, fulltime, and comments, but we're not using those here). The three <MvASSIGN> tags assign values to three variables whose names are of the form db_alias.d.field_name, where db_alias is a database alias ('worker_db' in this case), d is a special prefix standing for 'database', and field_name is the name of a database field. Since 'worker_db' matches the name of the current primary database, and the three field_names match the names of fields in the primary database, <MvADD> stores the three assigned values in the new record.

The index files idx1.mvx and idx2.mvx associated with the database are updated when an <MvADD> is executed.


Note: Assigning values to variables for addition to a database must be done after the database is opened.

The db_alias and d prefixes are optional for variables whose values are to be stored in the database, provided that the values are to be stored in the primary database. For example, the following two blocks of code would have worked the same as the one in the example above:

<MvASSIGN NAME="d.employee" VALUE="Walter Skinner">
<MvASSIGN NAME="d.title" VALUE="Assistant Director">
<MvASSIGN NAME="d.salary" VALUE="150000">
<MvADD>

<MvASSIGN NAME="employee" VALUE="Walter Skinner">
<MvASSIGN NAME="title" VALUE="Assistant Director">
<MvASSIGN NAME="salary" VALUE="150000">
<MvADD>

Variables of the form d.field_name or just field_name can be used to assign values to be stored in any database. By contrast, values of variables of the form db_alias.d.field_name will be stored only in the database with alias db_alias.

The longer form of the variable name is used to make your code more readable and easier to maintain. If you use the db_alias prefix there is no doubt about what the variable is being used for.

Date fields

Recall that for a date field fieldname, Miva Script creates variables fieldname_year, fieldname_month, fieldname_day, and fieldname_raw. fieldname_raw contains the date's value in yyyymmdd format. You can assign a value to a date field in three ways:

  1. Assign values to the fieldname_year, fieldname_month, and fieldname_day variables individually.
  2. Assign a value to fieldname_raw.
  3. Assign a time_t value directly to fieldname.

(In each case you must follow the variable assignment(s) with <MvADD>.)

The following two blocks of code have the same effect (assume that the active database worker_db has a date field called started):

<MvASSIGN NAME="worker_db.d.started_year" VALUE="1992">
<MvASSIGN NAME="worker_db.d.started_month" VALUE="12">
<MvASSIGN NAME="worker_db.d.started_day" VALUE="05">
<MvADD>

Or:

<MvASSIGN NAME="worker_db.d.started_raw" VALUE="19921205">
<MvADD>

Dates can also be assigned in time_t format. time_t is the number of seconds elapsed since the beginning of Coordinated Universal Time (C. U. T), at 00:00:00 on January 1, 1970. Miva supplies this value via the time_t and dyn_time_t built-in variables; time_t represents the time the script started running; dyn_time_t is updated dynamically when it is used in the script. The built-in function mktime_t generates a time_t value, given the date, time, and timezone. If you know the time_t value for the date you want to assign, you can assign it to the database variable directly. For example:

<MvASSIGN NAME="worker_db.d.started" VALUE="{time_t}">
<MvADD>

(Notice that a value is assigned directly to the started variable.)


Note: When you make an assignment of this kind, the value actually stored in the date variable (and the database) is the time_t value as of 00:00:00 on the date in question, not the exact time_t value that was assigned. The following code will display two different values (unless the script is run exactly at midnight!):
<MvEVAL EXPR="{time_t}">
<MvASSIGN NAME="d.datevbl" VALUE="{time_t}">
<MvEVAL NAME="{d.datevbl}">


Note: The same set of variables is used for both reading and writing to a record. Since these variables automatically adopt the values of the fields of the current record, any variables in this set that you do not explicitly assign values to before doing an <MvADD> will still have the values for the current record, and these will be written to the new record. For this reason, you should assign all relevant variables a value before using <MvADD>. Variables for fields to which you do not want to assign an explicit value at this time should be assigned null values.

Updating records - MvUPDATE


Summary

<MvUPDATE NAME="db_alias">

Modifies the current record of the database that has alias db_alias, with the contents of the variables corresponding to the database fields. If NAME is omitted, the current record in the primary database is updated. <MvUPDATE> locks the record that is being updated. <MvUPDATE> is an empty tag.


Here is an example that modifies a database record:

<MvOPEN
    NAME="worker_db"
    DATABASE="hr/dbs/workers.dbf"
    INDEXES="nameidx.mvx">
<MvFIND VALUE="Skinner">
<MvASSIGN NAME="worker_db.d.title" VALUE="Director"
<MvASSIGN NAME="worker_db.d.salary" VALUE="250000">
<MvUPDATE>

In this example, the database is opened with <MvOPEN> and the desired record is found with <MvFIND>. Then values are assigned to two variables corresponding to the title and salary fields in the worker_db database: worker_db.d.title is set to 'Director' and worker_db.d.salary is set to '250000'. Finally <MvUPDATE> stores these values in the current record.

Deleting a record - MvDELETE/MvUNDELETE/MvPACK


Summary

<MvDELETE NAME="db_alias">
<MvUNDELETE NAME="db_alias">
<MvPACK NAME="db_alias">

<MvDELETE> marks for deletion the current record in the database that has alias db_alias. Records marked for deletion accumulate until they are actually deleted from the database with <MvPACK>. These records are also visible to database searching and navigation until the database is packed. If the current record has been marked for deletion, the boolean variable db_alias.d.deleted is set to true (deleted or d.deleted can be used when referring to the primary database).

<MvUNDELETE> removes a mark set with <MvDELETE> from the current record in the database that has alias db_alias, stopping the record from being deleted by the next <MvPACK>.

<MvPACK> permanently removes all records in the database that have alias db_alias that have been marked for deletion with <MvDELETE>. Changes in database file size and in record counts resulting from <MvPACK> are not recorded until the active database is closed. All open index files are automatically updated after <MvPACK >is executed. Use <MvREINDEX> to update closed indexes.

For each of these tags, if NAME is omitted, the action is applied to the primary database. All of these tags are empty tags.



Tip: When a database is packed, physical record numbers (as provided by the recno built-in variable) are reset as required. For this reason, you should not code in a way that uses the physical record number as a unique identifier by which a record can be referred to. You may instead wish to define a database field in which you store a unique identifier for each record.


Tip: Because <MvPACK> can be quite resource-intensive for large databases, and does not automatically lock the database and index files that it uses, it should be used off-line, with Miva Mia. You can then re-install your database on your server. If you do pack the database on-line, you should use <MvLOCKFILE> to request a lock on the database and index files while <MvPACK> is executing.



Using database indexes

When records are added to a database, they are simply added at the bottom of the 'logical' table structure. Unless you explicitly added them in a specific order, the records will not be ordered in any way. Often you will want to be able to group or order records according to the values of certain fields (for example, to sort names alphabetically, or group together all employees with the same job title). For this you can create an index: instead of physically reordering the records, a database index changes the way the order appears to various Miva Script database commands.

An index performs a logical (rather than physical) reordering of the records in a database. This means that the order of the records in the database file doesn't actually change; however, if an index is open, some of the commands that operate on the database behave as if the ordering had changed: specifically, <MvSKIP> and <MvFIND> (in fact, <MvFIND> requires an index).

The reordering is based on the values returned by a key expression that is evaluated when the index is created or updated: these values can be, and often are, the values in a single field, but they can also be the concatenated values in two or more fields, or some other manipulation of the field values.

A database can have one or more indexes. You would create more than one index if you wanted to be able to switch indexes to search on different index key expressions. Even though a database can have several indexes open at the same time, only one of these will be the main or controlling index. The main index is the index that is used when you are searching the database.

All open indexes are updated automatically and immediately if the contents of the database are changed. In particular, if the indexed field of the current record changes, the record's position in indexed order may change, but the record pointer will continue to point to that record. This has an impact on the results given by the database navigation tags <MvFIND> and <MvSKIP>.

If you are opening a database only for searching, there is no need to open more than one index. If you are opening a database for updating, you should open all indexes that you may wish to use at any point with this database, so that they will be correctly updated (they can also be re-generated later). You can open indexes for a database (and make one of them the main index) using the <MvOPEN> and <MvSETINDEX> tags.

You should not attempt to modify index files directly, as this is likely to corrupt the index.

Creating a database index - MvMAKEINDEX


Summary

<MvMAKEINDEX
    NAME="db_alias"
    INDEXFILE="filename"
    EXPR="{key_expr}"
    FLAGS="[ascending|descending],[unique|nounique],[string]" > 

Creates an index file filename for the database with alias db_alias. If NAME is omitted, the index is created for the primary database. In this index, records are arranged in order according to the value of the key expression key_expr, and any flags that are present. INDEXFILE and EXPR are required. The database that the index applies to must exist and be open under the alias db_aliaswhen the index is created, though it need not contain any records. The newly-created index becomes the main index for that database. We recommend using the extension .mvx for index files. <MvMAKEINDEX> is an empty tag.


A key expression is a Miva Script expression that will be evaluated for each record in the database, and the records will be ordered in the index according to the results of these evaluations. A key expression can be any Miva Script expression; except in unusual circumstances, the key expression will be in some way based on field values in the database. A common choice would simply be a variable corresponding to one of the fields in the database.

The key expression may not exceed 500 characters in length, and may not evaluate to more than 100 characters.

If you have a database aliased as worker_db that has a field called salary, you could create an index that orders the record according to the value of the salary field, as follows:

<MvMAKEINDEX
    NAME="worker_db"
    INDEXFILE="salary.mvx"
    EXPR="{worker_db.d.salary}"

Suppose the sequence of records before indexing were:

Childs 50000
Kostick 30000
Kerr 100000
Yan 15000

After indexing, the apparent sequence of records will be:

Yan 15000
Kostick 30000
Childs 50000
Kerr 100000

For each record, the variable worker_db.d.salary is evaluated and the records ordered accordingly.


Tip: Remember to put curly brackets, '{' and '}', around index key expressions.

A variable corresponding to a field in a key expression can be written in the form db_alias.d.fieldname, d.fieldname, or just fieldname. The first form refers only to the database with alias db_alias, and the other two refer to the current primary database. An advantage to using the d.fieldname (or fieldname) form is that if the same physical database is opened under a different alias, the index will still be calculated correctly. However, if you use this form you must ensure that when you're updating the database in question, either (1) it is the primary database, or (2) the primary database does not also have a field called fieldname: if it does, then the value for d.fieldname will come from the current record in the primary database, rather than from the database being updated. It's up to you which form of the variable you choose to use, but in each case you need to take care to avoid unintended results.

You can index a database on more than one field by concatenating the values of these fields in the key expression. If you have a database called users that has fields firstname and lastname, you could index on the full name as follows:

<MvMAKEINDEX
    NAME="users"
    INDEXFILE="names.mvx"
    EXPR="{users.d.lname $ users.d.fname}">

Here the key expression concatenates the firstname with the last name and the index is built according to the results of these expressions. If two last names are the same they will be subindexed based on the first name.

Flags

The FLAGS attribute lets you supply further instructions for creating the index file.

  • ascending (default): By default, indexed records are arranged in ascending order, with lowest key values at the beginning of the index. For CHAR keys, 'A' is first; for NUMBER keys, the lowest numbers are first; and for DATE keys, the earliest date is first.

    Within an index having a key expression of type CHAR, records are not ordered alphabetically, but rather according to the ASCII codes of the characters in the key. Therefore 'A' through 'Z' precede 'a' through 'z'.

  • descending: the reverse of ascending.
  • unique: Allows only one record containing the results of the key expression to be stored in the index file.
  • nounique (default): all records containing the results of the key expression will be indexed, whether the key fields are unique or not. For write operations unlike the one in the code example above, where the "unique" flag is not set, no record will be excluded from being written to the database.
  • string: force string (rather than numeric) comparisons of key expressions when ordering the index.

At the end of an indexing operation, the index file is active and the record pointer is positioned at the top record of the indexed database.


Tip: Because <MvMAKEINDEX> can be quite resource-intensive for large databases, and does not automatically lock the database and index files that it uses, it should be used off-line, with Miva Mia. You can then re-install your database on your server. If you do create the index on-line, you should use <MvLOCKFILE> to request a lock on the database and index files while <MvMAKEINDEX> is executing.

Setting a new main index - MvSETINDEX


Summary

 <MvSETINDEX
    NAME="db_alias"
    INDEXES="f1.mvx,f2.mvx,...">

The specified index files become the index files for the database opened with alias db_alias. If NAME is omitted, the index files are opened for the primary database. The first file listed becomes the main index for the database. <MvSETINDEX> closes any open index files for this database before opening the specified index files. The record pointer moves to the first record in the database (in indexed order) after <MvSETINDEX> is executed. <MvSETINDEX> is an empty tag.

<MvOPEN> and <MvMAKEINDEX> will also open listed index files for the specified or primary database.


Recreating index files - MvREINDEX


Summary

<MvREINDEX
    NAME="db_alias"> 

<MvREINDEX> recreates all open index files for the database with alias db_alias, updating them to reflect the current contents of the database. If NAME is omitted, the index files for the primary database are recreated. Use <MvREINDEX> to incorporate all changes made to the database into the open index file(s), if they were not open when the database was updated. <MvREINDEX> is an empty tag.



Tip: Because <MvREINDEX> can be quite resource-intensive for large databases, and does not automatically lock the database and index files that it uses, it should be used off-line, with Miva Mia. You can then re-install your database on your server. If you do re-create the index on-line, you should use <MvLOCKFILE> to request a lock on the database and index files while <MvREINDEX> is executing.



Moving around in a database

This section explains several methods for navigating to specific records in a database. See also the section Configuring runtime error handling for information on how to test the results of a database navigation tag.

Finding records - MvFIND


Summary

<MvFIND
   NAME="db_alias"
   VALUE="search_value" 
   EXACT="EXACT">

<MvFIND> performs a case-sensitive search for search_value in the index of the database with alias db_alias, and moves the record pointer to the first matching record in the database. If NAME is omitted, the primary database's index will be searched. To succeed, the search must match starting at the first character in the indexed value. The first record found by <MvFIND> will differ depending on which index is the controlling index. At least one index must be open for the database being searched. <MvFIND> is an empty tag.

If the EXACT flag is set, the search will succeed only if search_value matches the entire indexed value, rather than just a substring starting at the first character. When searching for a number, set the EXACT flag.


<MvOPEN
   NAME="worker_db"
   DATABASE="hr/dbs/workers.dbf"
   INDEXES="names.mvx">
<MvFIND NAME="worker_db" VALUE="Mary">

In this example, if the index file names.mvx contains an index based on the employee field, the <MvFIND> will search for the first record in the database for which the employee name starts with 'Mary'.

Since <MvFIND> always finds the first matching record, subsequent calls to <MvFIND> will find the same record again. Therefore, if you want to find all records matching a particular value, you should use other techniques (such as <MvSKIP> in conjunction with an <MvWHILE> loop) to locate the rest of the records in the database that match. The advantage of starting with an <MvFIND> is that you can quickly locate the first such record. Since the database is indexed, you can be certain that other matching records will directly follow this one in indexed order. (<MvSKIP> will skip in indexed order if the database is indexed.)

This example uses <MvFIND> to locate the first record in which the name starts with 'Mary', and then uses an <MvWHILE> loop to find the others.

<MvCOMMENT>Open the database.</MvOMMENT>
<MvOPEN NAME="worker_db" DATABASE="workers.dbf">
<MvCOMMENT>Create an index based on the employee (employee name) field
</MvCOMMENT>
<MvMAKEINDEX NAME="worker_db" INDEXFILE="names.mvx" EXPR="{d.employee}">
<MvCOMMENT>Find the first 'Mary': MvFIND searches in the index.</MvCOMMENT>
<MvFIND NAME="worker_db" VALUE="Mary">
<MvCOMMENT>Now loop through all the records starting with 'Mary'
by testing that the first four characters of worker_db.d.employee
consist of the string 'Mary'. Since the database is indexed, we 
know that all such records are adjacent.</MvCOMMENT>
<MvWHILE 
    EXPR="{NOT worker_db.d.eof  
           AND substring(worker_db.d.employee, 1, 4) EQ 'Mary'}">
   <P><B><MvEVAL EXPR="{worker_db.d.employee$' - '$worker_db.d.title}"></B><P>
   <MvSKIP>
</MvWHILE>

Tip: To check whether an <MvFIND> was successful, test the value of the variable db_alias.d.eof (you can use d.eof or eof for the primary database); if it has the value 1 (true), the end of the database file was reached, and the search was unsuccessful. After an unsuccessful <MvFIND>, the record pointer points at the first record in the index. You can also test the value of the variable mvfind_error; it will have the value 'EOF' if the <MvFIND> did not succeed, and null otherwise.


Tip: <MvFIND> is always case-sensitive. If you want to do a case-insensitive search, you can create an index whose key expression converts all values to uppercase or lowercase, and then search using values in the desired case. The built-in string functions toupper and tolower will help you do case conversion.

Interaction between indexing and navigation

All open indexes are updated automatically and immediately if the contents of the database are changed. In particular, if the indexed field of the current record changes, the record's position in indexed order may change, but the record pointer will continue to point to that record. This has an impact on the results given by <MvFIND> and <MvSKIP>.

Suppose a database index contains the following entries, corresponding to the database's name field:

Aardvark
Dog
Giraffe
Zebra

Consider the following code:

<MvFIND VALUE="Dog">
<MvSKIP>
<MvEVAL EXPR="db_alias.d.name">

This will display the value 'Giraffe', since the <MvSKIP> skips to the record following 'Dog' in the index. Now let's see what happens if a record is changed:

<MvFIND VALUE="Dog">
<MvASSIGN NAME="db_alias.d.name" VALUE="Octopus">
<MvUPDATE>
<MvSKIP>
<MvEVAL EXPR="db_alias.d.name">

This will display the value 'Zebra'. Remember that when the name field of the 'Dog' record is changed, the index is immediately updated, as follows:

Aardvark
Giraffe
Octopus
Zebra

However, the record pointer still points to the same record, even though that record is at a different position in the index. Since the <MvSKIP> skips to the record following the current record ('Octopus') in the index, it now skips to the 'Zebra' record.

Filtering records - MvFILTER


Summary

<MvFILTER
   NAME="db_alias"
   FILTER="{expression}">

Makes all records in the database with alias db_alias that do not match the filter condition expression invisible to <MvFIND>, <MvSKIP>, and other database navigation tags; these tags will then find only the records that match both their own search condition and the filter condition. If NAME is omitted, the specified filter applies to the primary database.

If you have several open database aliases, each one can have a filter associated with it. However, a single alias can have only one filter applied to it at a time; a subsequent <MvFILTER> referring to that alias will replace the current filter. To remove a filter and not replace it, omit the FILTER attribute. (If both NAME and FILTER are omitted, the current filter for the primary database will be removed.)

<MvFILTER> is an empty tag.

The variables totrec and recno will continue to refer to the complete, unfiltered database.


If you want to find a series of records that all match a certain criterion, you have two basic options:

  1. Create an index sorted on whatever field(s) have the value you want to find, use <MvFIND> to locate the first record that matches your criterion, then use <MvWHILE> (using the same criterion that you used with <MvFIND>) to loop through the database records until you've read all the matching records.
  2. If the value you want to find does not match at the beginning of a field value (it might appear anywhere in the field), you can use <MvFILTER>.

Here is a small example that illustrates the use of <MvFILTER>. Suppose the database contains the following records, in indexed order, ordered by the value of the salary field:

Name Title Salary
Diamond Bottle Washer 12000
Novak Plongeur 15000
Maloney Dish Washer 15000
Barr Maitre d' 35000
Tchobanian Chief Cook 40000
Rabinovitch Window Washer 45000

If <MvFIND VALUE="15000"> is executed, it will move the record pointer to the 'Novak' record.

Now apply a filter:

<MvFILTER FILTER="{'Washer' CIN db_alias.d.title}">

All of the records that do not satisfy the filter condition ("Title contains 'Washer'") will become 'invisible' (even though they're still in the database, and in the same locations). The database now effectively looks like:

Name Title Salary
Diamond Bottle Washer 12000
Maloney Dish Washer 15000
Rabinovitch Window Washer 45000

Run the <MvFIND> again:

<MvFIND VALUE="15000">

Now the record pointer will be at the 'Maloney' record. Since the 'Novak' record does not match the filter condition, it is invisible to <MvFIND>.

This example uses an indexed database, but <MvFILTER> itself can be used with indexed or un-indexed databases.


Note: The variable recno cannot be used in a filter expression.


Note: The effect on the currently applied filter of accessing a record that the filter does not apply to is undefined.

Moving to a specific record number - MvGO

Summary


 <MvGO 
    NAME="db_alias" 
    ROW="row_number"
    VIEW="odbc_db_view"> 

Moves the record pointer to the record whose position in physical order is row_number, in the database with alias db_alias, regardless of how the database is indexed. If NAME is omitted, the record pointer for the primary database is moved. The special values 'top' and 'bottom' for row_number move the record pointer to the first and last database record, respectively. <MvGO> is an empty tag.



Note: In general, the ROW value specified with <MvGO> refers to the row number in physical order, not indexed order. There are two exceptions to this: if the database is indexed, the special values 'top' and 'bottom' will move the record pointer to the first and last records in indexed order. It follows from this that 'top' and 'bottom' do not move the record pointer to the physical top and bottom of an indexed database. In this situation, you can achieve the same effect via the following:
<MvGO ROW="1">
<MvGO ROW="{totrec}">


Note: <MvGO> is supported for ODBC databases. A value for the VIEW attribute is not required when working with non-ODBC databases.

Skipping to a record - MvSKIP


Summary

<MvSKIP
    NAME="db_alias"
    ROWS="number"
    VIEW="odbc_db_view">

<MvSKIP> moves the record pointer of the database with alias db_alias a specific number of records forward or backward relative to its current position. If NAME is omitted, the record pointer for the primary database is moved. If the database is not indexed, the pointer moves according to physical record number. If the database is indexed, the pointer moves according to the order in which records are indexed. If ROWS is omitted, the pointer moves one record forward. If ROWS is negative, the pointer moves backward. <MvSKIP> is typically used for constructing loops to sequentially access file records. <MvSKIP> is an empty tag.



Note: <MvSKIP> is supported for ODBC databases. A value for the VIEW attribute is not required when working with non-ODBC databases.



Obtaining a database's structure - MvREVEALSTRUCTURE


Summary

<MvREVEALSTRUCTURE
   NAME="db_alias"
   DATABASE="filename">

<MvREVEALSTRUCTURE> creates a database with filename filename (required) that contains information about the structure (as defined by <MvCREATE>) of the source database with alias db_alias. If NAME is omitted, the primary database is assumed. The newly created database contains one record for each field in the source database. The source database must be open when <MvREVEALSTRUCTURE> is executed. <MvREVEALSTRUCTURE> is an empty tag.


<MvREVEALSTRUCTURE> creates a new database with information about the open source database (db_alias). The records in this database correspond to the field definitions used in the <MvCREATE> tag that created the source database; there will be one record for each field definition for the source database. <MvREVEALSTRUCTURE> does not open this new database. If you want to access information in the new "MvREVEALSTRUCTURE" database, use <MvOPEN> to open it.

Each record will contain the following fields:

  • field_name: The name of a field in the source database.
  • field_type - A letter that specifies the type of field:
    • C - Character data (CHAR)
    • N - Numeric data (NUMBER)
    • D - Date (DATE)
    • L - Boolean data (BOOL)
    • M - Memo data (MEMO)
  • field_len - The number of characters allowed in the field; for CHAR fields, this is the number of characters specified in the field definition; for NUMBER fields, this is the sum of the number of characters before and after the decimal, plus 1 (for the decimal); the other types of fields have fixed values for field_len: 8 for DATE, 1 for BOOL, and 10 for MEMO.
  • field_dec - For numeric data only, the number of decimal places allowed to the right of the decimal point.


Accessing ODBC datasources

Miva Script access to ODBC datasources is currently supported only with Miva Mia.



Connecting to an ODBC datasource


Summary

<MvOPEN
    NAME="db_alias" 
    TYPE="odbc"
    DATABASE="odbc_connection_string">

Connects to an ODBC datasource. The value for the DATABASE attribute is an ODBC connection string. For example:

DSN=Sample;UID=dba;PWD=sqlpwd

This string will connect to an ODBC datasource called 'Sample', using the user id 'dba' and the password 'sqlpwd'. Some ODBC drivers may allow or require additional values.




Running SQL queries

Miva Script enables you to run SQL queries with ODBC datasources. SQL queries fall into two categories: those that return results and those that don't. Miva Script treats these two cases differently.


Summary

<MvQUERY
    NAME="db_alias"
    QUERY="SQL query">

<MvQUERY> runs an SQL query that does not return any results, such as an update, delete, or insert. QUERY is required. If NAME is omitted, the query applies to the primary database (this must be an ODBC datasource). <MvQUERY> is an empty tag.


For example:

<MvQUERY 
    NAME="my_db"
    QUERY = "create table Sample
             (Sample_ID integer,
              Sample_Name char( 100 ) )">

SQL queries that return results are handled by first opening a view. A view is like a 'virtual database' or snapshot of a subset of a database (or perhaps the entire database) as retrieved by a particular SQL query. Subsequent SQL queries refer to the view.


Summary

<MvOPENVIEW
    NAME="db_alias"
    VIEW="viewname"
    QUERY="sqlquery">
<MvSKIP VIEW="viewname" ...>
<MvGO VIEW="viewname" ...>
<MvCLOSEVIEW
   NAME="db_alias"
   VIEW="viewname">

<MvOPENVIEW> opens a view viewname based on the results of the query sqlquery. <MvSKIP> and <MvGO> can be used to navigate the view. <MvCLOSEVIEW> closes the view. <MvOPENVIEW> and <MvCLOSEVIEW> are empty tags.


Here is an example of opening a view:

<MvOPENVIEW
    NAME="my_dbs"
    VIEW="sampleview"
    QUERY="select * from Sample">

The results of applying the query 'select * from Sample' are assigned the name 'sampleview'.

Once you have opened a view, you can use the familiar <MvSKIP> and <MvGO> tags to navigate through the results.

For example:

<MvSKIP VIEW="sampleview" ROWS="1">
<MvGO VIEW="sampleview" ROW="top">

The variable eof will be true when the end of the view has been reached, and recno will indicate the current position in the view. totrec and deleted, available for regular (xbase3) Miva databases, are not supported with ODBC datasources.

When you are done with a view, close it using <MvCLOSEVIEW>:

<MvCLOSEVIEW VIEW="sampleview">

The following tags allow the optional VIEW attribute, but currently only <MvGO> and <MvSKIP> can be used with views:

  • <MvSKIP>
  • <MvGO>
  • <MvPRIMARY>
  • <MvFIND>
  • <MvSETINDEX>
  • <MvUPDATE>
  • <MvDELETE>
  • <MvUNDELETE>
  • <MvADD>
  • <MvREVEALSTRUCTURE>


Internet commerce - <MvCOMMERCE>


Summary

<MvCOMMERCE
   ACTION="url_to_commerce_server"
   METHOD="access_method"
   FIELDS="value1,value2,...">
...
...<MvCOMMERCESTOP>...
...
</MvCOMMERCE>

The <MvCOMMERCE> tag facilitates Internet commerce by providing an interface for accessing commerce servers.

  • ACTION - The URL for the destination server. (optional)
  • METHOD (required) - Identifies the method you are using to contact the server (that is, the type of service you are requesting). Define the METHOD to match the one you have (or will) registered in the Miva Engine.
  • FIELDS - List of variables that contains the data to be sent to the commerce library.

<MvCOMMERCE> can loop more than one time. It will continue to loop until it no longer receives data, or until it is explicitly halted with the tag.

The commerce library performs its functions and passes the data back to the Miva Script application. The output from the commerce library is a binary tree that allows the library to make results available to a Miva Script application.



Commerce Library Exported Functions

A commerce library communicates with a Miva Script application through the Miva Engine. When the Miva Engine encounters the <MvCOMMERCE> tag in a Miva Script application, it consults its list of registered commerce libraries. If a matching commerce library is found, the Miva Engine loads the corresponding DLL. To develop a commerce library for a Miva Script application, the DLL must export the following four functions:

  • miva_commerce_init
  • miva_commerce_loop
  • miva_commerce_cleanup
  • miva_commerce_error
All communication from a Miva commerce library to the Miva Script application is through these four exported functions.

miva_commerce_init

This function is called when the Miva Engine encounters a <MvCOMMERCE> block. Its purpose is to initialize any data that will be used inside the <MvCOMMERCE> block. The DLL can store any application-specific values in the "data" parameter. This parameter is passed to the other exported functions.

Syntax

    Miva_Commerce_Status miva_commerce_init ( Miva_Context context, 
    void **data, 
    const char *method, 
    const char *action, 
    Miva_VariableList input, 
    Miva_VariableTree output ); 

Parameters

  • context -- a handle to Miva_Context.
  • data -- a placeholder that allows a commerce library to allocate and store instance-specific data. Any value, which you assign to the data parameter, is passed to any subsequent calls to the commerce library.
  • method -- the value of the METHOD attribute that was specified in the <MvCOMMERCE> tag.
  • action -- the value of the ACTION attribute that was specified in the <MvCOMMERCE> tag.
  • input -- a handle to a Miva_VariableList containing all the variables specified in the FIELDS parameter of the <MvCOMMERCE> tag.
  • output -- a handle to a Miva_VariableTree which is used by the commerce library to return values to the module. All variables added to this tree are made available to the module in the form of system variables, which are available only inside the <MvCOMMERCE> block.

Return Value

  • MIVA_COMMERCE_OK - successful, then proceeds with executing the code inside the <MvCOMMERCE> block.
  • MIVA_COMMERCE_ERROR - one of more errors were encountered and the Miva Engine calls miva_commerce_error.

miva_commerce_loop

When miva_commerce_init returns MIVA_COMMERCE_OK, the Miva Engine calls the miva_commerce_loop function. This function is called once per iteration of a <MvCOMMERCE> block. The parameter ìiterationî indicates the number of times that miva_commerce_loop has been called.

Syntax
    Miva_Commerce_Status miva_commerce_loop ( Miva_Context context, 
    void **data, 
    const char *method, 
    const char *action, 
    Miva_VariableList input, 
    Miva_VariableTree output, 
    int iteration ); 
Parameters
  • context -- a handle to Miva_Context
  • data -- a placeholder that was passed to miva_commerce_init. If you assigned a value to it there, it will have the same value here.
  • method -- the value of the METHOD attribute that was specified in the <MvCOMMERCE> tag.
  • action -- the value of the ACTION attribute that was specified in the <MvCOMMERCE> tag. input a handle to a Miva_VariableList containing all the variables specified in the FIELDS parameter of the <MvCOMMERCE> tag.
  • output -- a handle to a Miva_VariableTree which is used by the commerce library to return values to the module. All variables added to this tree are made available to the module in the form of system variables only inside the <MvCOMMERCE> block.
  • iteration -- the number of times Miva has executed the code inside the <MvCOMMERCE> block. The first time miva_commerce_loop is called, iteration has a value of 0.
Return Value
The action that the Miva Engine takes is dependent on the return value of the miva_commerce_loop.
  • MIVA_COMMERCE_OK -- the engine will proceed to execute the code inside the <MvCOMMERCE> block.
  • MIVA_COMMERCE_ERROR -- one of more errors were encountered and the Miva Engine calls miva_commerce_error.
  • MIVA_COMMERCE_END -- the engine will exit the commerce block normally (you would normally return MIVA_COMMERCE_END when your commerce library functions are complete and should stop).

miva_commerce_cleanup

Whenever the Miva Engine has completed using the commerce library, regardless of the return value, it calls miva_commerce_cleanup.

Syntax

    void miva_commerce_cleanup ( 
    Miva_Context context, 
    void **data, 
    const char *method, 
    const char *action, 
    Miva_VariableList input ); 
Parameters
  • context a handle to Miva_Context. Your commerce library will need this handle to use the networking and file functions provided by the Commerce API. data a placeholder that was passed to miva_commerce_init. If you assigned a value to it there, it will have the same value here.
  • method the value of the METHOD attribute that was specified in the <MvCOMMERCE> tag.
  • action the value of the ACTION attribute that was specified in the <MvCOMMERCE> tag. input a handle to a Miva_VariableList containing all the variables specified in the FIELDS parameter of the <MvCOMMERCE> tag.

Return Value

No return value.

miva_commerce_error

If either miva_commerce_init or miva_commerce_loop returns MIVA_COMMERCE_ERROR, the Miva Engine calls miva_commerce_error to receive a description of the error. It makes this call prior to calling miva_commerce_cleanup. This function is called by the Miva Engine to receive a description of an error. It is called prior to calling miva_commerce_cleanup.

Syntax

    const char *miva_commerce_error ( 
    Miva_Context context, 
    void **data, 
    const char *method, 
    const char *action, 
    Miva_VariableList input ); 
Parameters
  • context -- a handle to Miva_Context. Your commerce library will need this handle to use the networking and file functions provided by the Commerce API.
  • data -- a placeholder that was passed to miva_commerce_init. If you assigned a value to it there, it will have the same value here.
  • method -- the value of the METHOD attribute that was specified in the <MvCOMMERCE> tag.
  • action -- the value of the ACTION attribute that was specified in the <MvCOMMERCE> tag.
  • input -- a handle to a Miva_VariableList containing all the variables specified in the FIELDS parameter of the <MvCOMMERCE> tag.

Return Value

The text for the error is returned.

Using UPS Quick Cost

<MvCOMMERCE> supports the 'UPSCost' METHOD. This enables your program to submit information about a shipment (weight, origin, destination, etc.) to UPS's Quick Cost calculator and receive back the shipping cost and other information. <MvCOMMERCE> provides a convenient way to do pre- and post-processing of UPS shipping information without having to prepare your own CGI scripts to do so. UPS's documentation on Quick Cost is available from http://www.ups.com/tools/tools.html.


Note: Quick Cost can calculate costs only for shipments that originate in the United States.

The basic format of <MvCOMMERCE> is as follows:

<MvCOMMERCE
    ACTION="http://www.ups.com/using/services/rave/qcostcgi.cgi"
    METHOD="UPSCost"
    FIELDS="var1, var2,...">

The values of ACTION and METHOD must be exactly as indicated. The value of the FIELDS attribute is a series of input variables giving information about the shipment. These variables will generally get their values from a form in your document. There are specific variable names (listed below) that must be used; these variables are case-sensitive.

To set up UPS processing, follow these steps:

  1. Create a form that contains fields for each input variable (that is, each type of information) that you want to send to the Quick Cost calculator. Some variables are required; others are optional.
    1. The name (NAME attribute) of each field must be the same as the corresponding variable name.
    2. Choose form objects that are appropriate to the type of data: for example, check boxes for yes/no choices, text boxes for text, and radio buttons or drop-down lists for choices from a defined group.
    3. Set the form's ACTION attribute to point to the Miva Script program containing the <MvCOMMERCE> tag. If it's the same as the script containing the form, just use the macro &[documenturl]; as the value of ACTION.
    4. Set the form's METHOD attribute to 'POST'.
  2. Include each of the input variables from the form in the FIELDS attribute of <MvCOMMERCE>. The order is unimportant, and you need include only those variables that actually appear in the form.
  3. Between the <MvCOMMERCE> and </MvCOMMERCE> tags, insert code that processes the information sent back by the Quick Cost calculator. The information is sent back in the Return Fields listed below. You don't have to use all of these variables in your code, just the ones you need. The processing that you do can be as simple as just displaying values of certain variables, or you can do more complex processing as desired. As a minimum, you should probably display the cost (totalchrg), or an error message (errmsg) if there is one.

UPS input variables

This list gives the input variables that you can use in a form and pass to the Quick Cost calculator using the FIELDS attribute. The Value column describes the types of values that each variable must be given in the form. Required variables are in red. Contact UPS or consult their online documentation for detailed definitions and service descriptions.


Field Decription
AppVersion Set to 1.2.
AcceptUPSLicenseAgreement Must have the value 'yes' when the form is submitted.
ResponseType Data stream format to be used in repsonse message.
ActionCode 3 - Show rate for selected UPS service.
4 - Show rates for all available UPS services
Defines the level of service required in the response. services.
ServiceLevelCode Define type of UPS shipping
UPS Service___________UPS Product Code
Next Day Air Early_______1DM
Next Day Air____________1DA
Next Day Air Intra________1DAPI
Puerto Rico)
Next Day Air Saver_______1DP
2nd Day Air A M_________2DM
2nd Day Air_____________2DA
3 Day Select____________3DS
Ground ________________GRD
Canada Standard ________STD
Worldwide Express_______XPR
Worldwide Express_______XDM
Worldwide Expediated ____XPD
RateChart Determines which rate chart is used to calculate the rate.
Valid Codes are:
- Customer+Counter
- Letter+Center
- On+Call+Air
- One+Time+Pickup
- Regular+Daily+Pickup
ShipperPostalCode Postal code for origin of package.
ConsigneePostalCode One to six alphanumeric postal code of the destination country.
ConsigneeCountry Country code
PackageActualWeight Weight of package. If UPS letter, no weight required. If fraction included, will be rounded to next whole value.
DeclaredValueInsurance

All packages automatically insured for $100. Additional insurance $.35/$100 of value or fraction thereof. Max value for insurance $50,000.

Length Length of package in inches. Default = 0.
Width Width of package in inches. Default = 0.
Height Weight of package in inches. Default = 0.
OversizeInd

0 = Not Oversized
1 = Package Oversized

CODInd 0 = Not COD
1 = Shipment is COD
HazMat 0 = No hazardous material
1 = Package contains a hazardous material
AdditionalHandlingInd 0 = No special handling
1 = Package requires additional handling
CallTagARDInd 0 = for none
1 = for basic call tag service
2 = for electronic
SatDeliveryInd 0 = No Saturday delivery
1 = Saturday delivery
SatPickupInd 0 = No Saturday pickup
1 = Saturday pickup
DCISInd 0 = None
1 = Basic
2 = Signature Required
3 = Alternative Return Address
4 = All Available Information
VerbalConfirmationInd 0 = No verbal confirmation; Default
1 = Verbal confirmation
SNDestinationInd1 0 = None; Default
1 = Domestic
2 = International
SNDestinationInd2 0 = None; Default
1 = Domestic
2 = International
ReturnLabelInd 0 = No return label; Default
1 = Return label
ResidentialInd 0 = Commercial
1 = Residential
PackagingType 00 = Shipper Supplied Packaging
01 = UPS Letter Envelope
03 = UPS Tube
21 = International UPS 25KG Box
25 = International UPS 10KG Box


Return Fields

The following fileds are returned inside the <MvCOMMERCE>...</MvCOMMERCE>:

<MvCOMMERCE 
      METHOD = "UPSRSS" 
      ACTION = "http://www.ups.com/using/services/rave/qcost_dss.cgi" 
      FIELDS = "{ l.fields }">

Returned Fields

AcceptUPSLicenseAgreement = <MvEVAL EXPR = "{ g.AcceptUPSLicenseAgreement }">
UPSOnLine = <MvEVAL EXPR = "{ s.UPSOnLine }">
AppVersion = <MvEVAL EXPR = "{ s.AppVersion }">
ReturnCode = <MvEVAL EXPR = "{ s.ReturnCode }">
MessageText = <MvEVAL EXPR = "{ s.MessageText }">
ActionCode = <MvEVAL EXPR = "{ s.ActionCode }">
ServiceLevelCode = <MvEVAL EXPR = "{ s.ServiceLevelCode }">
ShipperPostalCode = <MvEVAL EXPR = "{ s.ShipperPostalCode }">
ShipperCountry = <MvEVAL EXPR = "{ s.ShipperCountry }">
ConsigneePostalCode = <MvEVAL EXPR = "{ s.ConsigneePostalCode }">
ConsigneeCountry = <MvEVAL EXPR = "{ s.ConsigneeCountry }">
DeliveryZone = <MvEVAL EXPR = "{ s.DeliveryZone }">
PackageActualWeight = <MvEVAL EXPR = "{ s.PackageActualWeight }">
ProductCharge = <MvEVAL EXPR = "{ s.ProductCharge }">
AccessorySurcharge = <MvEVAL EXPR = "{ s.AccessorySurcharge }">
TotalCharge = <MvEVAL EXPR = "{ s.TotalCharge }">
CommitTime = <MvEVAL EXPR = "{ s.CommitTime }">

Example

The file http://www.miva.com/products/engine/mia/templates/frame-ups.html gives an example of using UPSCost. Here is a shorter example that uses only required fields.

<HTML>

<HEAD><TITLE>UPS Cost Calculator</TITLE></HEAD>

<BODY BGCOLOR = "#ffffff">

<MvIF EXPR = "{ g.AcceptUPSLicenseAgreement EQ 'yes' }">
<MvCOMMENT> This branch is expected if the form has been submitted.</MvCOMMENT>

<TABLE BORDER = 0 WIDTH = "100%">
<TR><TD>
	<B>Product</B>
</TD><TD WIDTH = "100%">
	<B>Name</B>
</TD><TD>
	<B>Price</B>
</TD></TR>
<TR><TD COLSPAN = 3>
	<HR>
</TD></TR>

<MIVA MvCOMMERCE_Error = "nonfatal, nodisplay">


<MvCOMMERCE METHOD = "UPSRSS"
		      ACTION = "http://www.ups.com/using/services/rave/qcost_dss.cgi"
		      FIELDS ="AcceptUPSLicenseAgreement,
			    ActionCode,
			    ServiceLevelCode,
			    RateChart,
			    ShipperPostalCode,
			    ConsigneePostalCode,
			    ConsigneeCountry,
			    PackageActualWeight,
			    ResidentialInd,
			    PackagingType">

	<MvIF EXPR = "{ s.ReturnCode NE '0000' }">
		<MvASSIGN NAME = "g.Error" VALUE = "{ s.MessageText }">
		<MvCOMMERCESTOP>
	<MvELSE>

<TR><TD>
	<MvEVAL EXPR = "{ s.ServiceLevelCode }">
</TD><TD>
	<MvIF EXPR = "{ s.ServiceLevelCode EQ '1DM' }">
	Next Day Early AM
	</MvIF>
	<MvIF EXPR = "{ s.ServiceLevelCode EQ '1DA' }">
	Next Day Air
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ '1DAPI' }">
	Next Day Air Intra (Puerto Rico)
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ '1DP' }">
	Next Day Air Saver
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ '2DM' }">
	2nd Day Air AM
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ '2DA' }">
	2nd Day Air
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ '3DS' }">
	3 Day Select
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ 'GND' }">
	Ground
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ 'STD' }">
	Canada Standard
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ 'XPR' }">
	Worldwide Express
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ 'XDM' }">
	Worldwide Express Plus
	</MvIF>

	<MvIF EXPR = "{ s.ServiceLevelCode EQ 'XPD' }">
	Worldwide Expedited
	</MvIF>
	</TD><TD>
	<MvEVAL EXPR = "{ s.TotalCharge }">
	</TD></TR>
	</MvIF>
</MvCOMMERCE>

</TABLE>

<MvIF EXPR = "{ MvCOMMERCE_Error }">
	<P><B><MvEVAL EXPR = "{ MvCOMMERCE_Error }"></B></P>
<MvELSE>
	<MvIF EXPR = "{ g.Error }">
		<P><B><MvEVAL EXPR = "{ g.Error }"></B></P>
	</MvIF>
</MvIF>

<P><A HREF = "&[s.documenturl];">Calculate shipping for another package.</A></P>

<MvELSE>


<FORM METHOD = "post" ACTION = "&[s.documenturl];">
<TABLE BORDER = 0>
<TR><TD>
	 
</TD><TD>
<B>AcceptUPSLicenseAgreement</B>
	<INPUT TYPE = "checkbox" NAME = "AcceptUPSLicenseAgreement" VALUE = "yes" CHECKED> 
</TD></TR>

<TR><TD>
	<B>ActionCode:</B>
</TD><TD>	
	<INPUT TYPE = "radio" NAME = "ActionCode" VALUE = 4 CHECKED> All Services<BR>
	<INPUT TYPE = "radio" NAME = "ActionCode" VALUE = 3> Specified Service Only<BR>
</TD></TR>

<TR><TD>
	<B>ServiceLevelCode:</B>
</TD><TD>
	<SELECT NAME = "ServiceLevelCode">
	<OPTION VALUE = "1DM">Next Day Early AM
	<OPTION VALUE = "1DA">Next Day Air
	<OPTION VALUE = "1DAPI">Next Day Air Intra (Puerto Rico)
	<OPTION VALUE = "1DP">Next Day Air Saver
	<OPTION VALUE = "2DM">2nd Day Air AM
	<OPTION VALUE = "2DA">2nd Day Air
	<OPTION VALUE = "3DS">3 Day Select
	<OPTION VALUE = "GND">Ground
	<OPTION VALUE = "STD">Canada Standard
	<OPTION VALUE = "XPR">Worldwide Express
	<OPTION VALUE = "XDM">Worldwide Express Plus
	<OPTION VALUE = "XPD">Worldwide Expedited
	</SELECT>
</TD></TR>

<TR><TD>
	<B>RateChart:</B>
</TD><TD>	
	<SELECT NAME = "RateChart">
	<OPTION VALUE = "Customer Counter" SELECTED>Customer Counter</OPTION>
	<OPTION VALUE = "Letter Center">Letter Center</OPTION>
	<OPTION VALUE = "On Call Air">On Call Air</OPTION>
	<OPTION VALUE = "One Time Pickup">One Time Pickup</OPTION>
	<OPTION VALUE = "Regular Daily Pickup">Regular Daily Pickup</OPTION>
	</SELECT>
</TD></TR>

<TR><TD>
	<B>ShipperPostalCode:</B>
</TD><TD>
	<INPUT TYPE = "text" NAME = "ShipperPostalCode" SIZE = 10 VALUE = "92117">
</TD></TR>

<TR><TD ALIGN = "left">
<B>ConsigneePostalCode:</B>
</TD><TD ALIGN ="left">
	<INPUT TYPE = "text" NAME = "ConsigneePostalCode" SIZE = 10 VALUE = "44145">
</TD></TR>
<TR><TD ALIGN = "left">
	<B>ConsigneeCountry:</B>
</TD><TD ALIGN = "left">
	<INPUT TYPE = "text" NAME = "ConsigneeCountry" SIZE = 4 VALUE = "US">
</TD></TR>

<TR><TD ALIGN = "left">
	<B>PackageActualWeight:</B>
</TD><TD ALIGN = "left">
	<INPUT TYPE = "text" NAME = "PackageActualWeight" SIZE = 10 VALUE = "5">
</TD></TR>

<TR><TD>
	<B>ResidentialInd:</B>
</TD><TD ALIGN = "left">
	<SELECT NAME = "ResidentialInd">
	<OPTION VALUE = "0" SELECTED>Commercial</OPTION>
	<OPTION VALUE = "1">Residential</OPTION>
	</SELECT>
</TD></TR>

<TR><TD>
	<B>PackagingType:</B>
</TD><TD ALIGN = "left">
	<SELECT NAME = "PackagingType">
	<OPTION VALUE = "00" SELECTED>Shipper Supplied Packaging</OPTION>
	<OPTION VALUE = "01">UPS Letter Envelope</OPTION>
	<OPTION VALUE = "03">UPS Tube</OPTION>
	<OPTION VALUE = "21">UPS Express Box</OPTION>
	<OPTION VALUE = "24">International UPS 25KG Box</OPTION>
	<OPTION VALUE = "25">International UPS 10KG Box</OPTION>
	</SELECT>
</TD></TR>

<TR><TD>
	 
</TD><TD ALIGN = "left">
	<INPUT TYPE = "submit" VALUE = "Calculate">
</TD></TR>
</TABLE>
</FORM>

</MvIF>

</BODY>
</HTML>

Using CyberSource ICS

The <MvCOMMERCE> tag provides an interface to CyberSource Corporation's Internet Commerce Services (ICS). ICS is a collection of services that allow you to conduct reliable, safe, and secure transactions on the Internet. Each service is available individually; you subscribe only to those services you require.

The sections that follow contain outlines of ICS, the preparations you have to make to become ICS-enabled, and setting up ICS processing in Miva Script programs. Full ICS documentation is available at http://www.miva.com/cybersource. Many of the links below point to documents at that location.

ICS overview

The process starts when an end user places an order on your Web site through their web browser. Your Miva Script applications then request the appropriate services from ICS.

To use ICS, you subscribe to the services you need to conduct business from your electronic storefront. To use these services as an ICS subscriber using Miva Empresa, you set up your system and build the scripts that allow you to transmit information to, and receive and integrate information from, CyberSource's ICS servers. This is done by using the <MvCOMMERCE> tag to send a Simple Commerce Messaging Protocol (SCMP) message to an ICS server. The scripts can be implemented on any web server that is equipped with Miva Empresa.

SCMP specifies name/value pairs for specific CyberSource services and wraps this sensitive transaction information in an RSA encrypted envelope. The message is sent to CyberSource where CyberSource performs the services and returns the results back to you.

Becoming ICS-enabled

Before you can develop scripts that access ICS, several preparations must be made. Most importantly:

  • A merchant account must be set up with your company's bank. In order to use CyberSource's ICS Services, your bank must one that handles 'Card Not Present' (CNP) or 'mail-order/telephone-order' (MOTO) transactions. If your bank cannot process these types of credit card transactions with your account, you may want to select from the list of banks and financial institutions that are already familiar with the CyberSource processing gateways.
  • Basic information about products offered for sale must be available to you and be delivered to CyberSource. Some of this information is also described in the Product description (digital offer).
  • Your company must have subscribed for services with CyberSource. You can subscribe to any combination of the services described in this document.
  • Your product-ordering Web pages must be created.

Setting up ICS-processing scripts

The steps for setting up ICS processing in your scripts are as follows:

  1. Create an <MvCOMMERCE> tag (described below) that defines the SCMP message that you want to send.
  2. Typically, some of the information contained in the <MvCOMMERCE> tag will be predefined in your program (for example, the merchant identification) while other information (such as the customer name) will be gathered when the program is executed. You will need to create a mechanism (typically an HTML form) that will gather the second type of information.
  3. All the required information must be sent to the program containing the <MvCOMMERCE> tag, typically through a form submission. When the <MvCOMMERCE> tag is executed, it sends the message to the ICS server. The ICS server reorders the requested list as appropriate and checks the list against the list of services authorized for the requesting merchant.

    If the list is valid, the ICS server performs the services and returns the results for all services to your program. These results are available to your program as Miva Script variables.

  4. Place code between the <MvCOMMERCE> and </MvCOMMERCE> tags that checks the results returned by the ICS server for status and information.
  5. Test your scripts.

Setting up the <MvCOMMERCE> tag

To request an ICS service, you build an SCMP message with the <MvCOMMERCE> tag. Each service has its own detailed requirements, but you will request all services using the same general approach.

The attributes of <MvCOMMERCE> are set as follows:

  • ACTION - URL for the CyberSource ICS server that you are requesting services from. By default, all messages are sent to the ics.ic3.com ICS server. Do not change this unless directed to do so by CyberSource.
  • METHOD - Identifies how you are contacting the ICS server. Use 'SCMP' (Simple Commerce Messaging Protocol) to request ICS services.
  • FIELDS - Contains the list of fields that make up the SCMP message that you are sending for processing. Information about the services being requested, the merchant requesting the services, and the end user (customer) is sent in the message as a set of values for the list of fields.
    <MvCOMMERCE ACTION="http://ics.ic3.com"
        METHOD="SCMP"
        FIELDS="ics_svcs,
        ics_merchant,
        firstname,lastname,email,phone,address1,city,state,zip,
        country,ccnum,expmo,expyr,
        offer0,offer1,...,offerN">
    ...code...
    ...code...
    ...code...
    </MvCOMMERCE>

Each of the components of FIELDS is a Miva Script variable. These variables specify four types of information.

Merchant identification

The ics_merchant variable contains the name of your company's encryption key.

Services requested

The ics_svcs variable contains a comma-separated list of strings that specify the services desired for the order. For example:

<MvASSIGN NAME="ics_svcs" VALUE="ics_preapp,ics_bill">

ics_preapp and ics_bill are services.

You can choose any or all of the following services (for details, see SCMP and ICS Services Reference):

Service Name Description
ics_tax Calculate the sales tax for products in an order
ics_preapp Authorize credit, total the order, check export restrictions, and generate a download URL
ics_export Check export restrictions
ics_download Generate a download URL
ics_score Check an order's validity
ics_notify Notify a distributor to ship a physical product
ics_elc Generate an electronic license certificate (ELC) for the product to be downloaded from the URL
ics_bill Bill a credit card
ics_credit Issue a refund to a credit card

For best results, include all services for a single order in a single message. The ICS server will pass some information among the services that you would otherwise have to provide. The order of the services is not important if you are requesting them simultaneously.

Service-specific fields

Each service requires that certain fields be present in the SCMP message. These correspond to Miva Script variables included in the FIELDS attribute of <MvCOMMERCE>. Fields for each service are described in SCMP and ICS Services Reference. These variables must be assigned their values somewhere in your program before the <MvCOMMERCE> tag is executed; typically, these variables will get their values from an HTML form.

If more than one service uses the same fields (for example, several services use the fields firstname and lastname), you have to specify values for these fields only once. Fields describing the end user (your customer), the end user's credit card, or general information about the order tend to be used more than once.

Product descriptions

The variables offer0, offer1, offer2, ..., offerN contain a product description (digital offer) or each product in the current order.

Product descriptions consist of a string of name:value pairs, where the name and value are separated by a colon (:), and the pairs are separated by a caret (^) character. You can include as many product descriptions as you need.

This example defines two digital offers:

  • The ID of the first product (offer0) is '1', its price is $495.00, and its product name is 'Miva Engine'.
  • The ID of the second product (offer1) is '2', its price is $495.00, and its product name is 'Miva Merchant'.
<MvASSIGN NAME="offer0"
VALUE="{'offerid:1^productname:Miva Engine^' $ 
'productsku:mivaeng^producttype:electronic^' $ 
'price:495.00^quantity:1'}">

<MvASSIGN NAME="offer1"
VALUE="{'offerid:2^productname:Miva Merchant^' $'productsku:kclic^producttype:electronic^' $
'price:99.00^quantity:1'}">


Using CyberCash CashRegister

Miva Script provides an interface to the CyberCash CashRegister API, which supports a variety of e-commerce services. The sections that follow contain an overview of the available operations and how to access them from Miva Script. See the CashRegister Service: Development Guide from CyberCash at http://www.cybercash.com/cybercash/merchants/docs/dev.pdf

For complete CashRegister support documantation, visit http://www.cybercash.com/cybercash/merchants/support/doclib.html.


Note: <MvCOMMERCE> supports only the CashRegister API, and not DirectPay.

Getting started

Before you can use CyberCash services, you must do the following:

  1. If you have not done so already, obtain a merchant account from your financial institution (this allows you to accept credit card transactions).
  2. Register with CyberCash Inc. (you may wish to do this through your Miva provider). You will receive a merchant ID and merchant key.
  3. In your Miva data directory, create a file CyberCash\<merchant-id>.key, containing your merchant key. For example, if your merchant ID is 'test-mck', you must have a file called CyberCash\test-mck.key.

Calling CyberCash

The basic format of a call to CyberCash is as follows:

<MvCOMMERCE
    ACTION="http://cr.cybercash.com/"
    METHOD="CyberCash"
    FIELDS="cybercash-id,operation,[operation-specific-input-fields]">
...
</MvCOMMERCE>

cybercash-id contains your CyberCash merchant ID. operation contains a string describing the CyberCash operation that is being requested (this string is referred to as a 'message'). Each service has a number of input variables that should be included in the FIELDS attribute. CyberCash will return a number of output variables. Miva Script can process these variables in the <MvCOMMERCE>...</MvCOMMERCE> block. If an operation returns multiple sets of results, <MvCOMMERCE> will loop once for each set.


Note: Many CyberCash-related variables contain the hyphen (-) character. Normally this is an illegal character in Miva Script variables, and therefore should be 'escaped' with a backslash when it occurs in an expression. For example:
<MvEVAL EXPR="{card\-exp}">



CyberCash operations

To specify a CyberCash operation with <MvCOMMERCE>, set the cybercash-id variable to the name of the operation, as a literal string. For example:

<MvASSIGN NAME="cybercash-id" VALUE="mauthonly">

There are three kinds of operations: those that implement a particular commerce transaction; those that query a database of transactions; and batch operations that allow several transactions or queries to be submitted at once. Here are a selection of operations:

batch-commit Submit several transactions at the same time
checkauth Validate and authorize a check payment
mauthcapture Authorize and capture a credit card sale
query Query the transaction database
return Return money to a credit card
void Void a transaction




Input variables

Each operation has a specific set of input variables associated with it. These variables must appear in the FIELDS attribute of the <MvCOMMERCE> tag.

order-id Unique order ID
amount Amount to authorize
card-number Credit card number
card-exp Credit card expiry date
card-name Name on credit card
card-address Card holder's street address
card-city Card holder's city
card-zip Card holder's ZIP code or other postal code
card-state Card holder's state
card-country Card holder's country




Output variables

For each operation, CyberCash returns a specific set of output variables. These variables are available inside the <MvCOMMERCE> loop. These variables are described in the sections documenting each operation, in Appendix B of the CashRegister Service: Development Guide. Some common output variables for transactions are:

mstatus Status code
merrloc Error location
merrmsg Error message
merch-txn A transaction identifier
order-id Order ID
card-type Type of credit card used
card-number Credit card number
card-exp Credit card expiry date
auth-code Authorization code
action-code Action code returned by the financial institution



Formatting strings - FMT operator


Summary

{string FMT pattern}

Format string according to the format pattern pattern.


Miva Script provides the FMT operator to format strings. For example, you may have a value of which you want to display only the first four characters:

<MvASSIGN name="tex" value="t56hkl0p">
<MvEVAL EXPR="{tex FMT '4.'}">

The FMT expression contains a value (the variable tex) and a pattern, the string '4.'. FMT tries to match the pattern to the characters in the value of tex, starting at the leftmost character. The pattern in this example is a very simple one, and simply matches the first four characters, no matter what they are. The result of the expression will be the characters 't56h'.

Formatting patterns can contain the following components:

  • A modifier that matches and displays a character.
  • A '!' , which reverses the meaning of the modifier that follows it
  • A number that specifies how many times the character should be matched.
  • Brackets ([ and ]) which group several patterns together
  • '&nnn' ,which displays the character with ASCII code nnn

Unlike patterns in some other programming languages, the whole pattern does not have to match in order to for it to have an effect. Miva Script matches as much of the pattern as it can, displays the text that was matched, and discards the rest.

FMT Patterns and Modifiers

Modifier Action
A Match a letter and display it in uppercase
a Match a letter and display it in lowercase
S Match and display a special character (the percent symbol, for example)
# Match and display a digit
P Match and display a digit, and if no character is found, pad a "0."
. Match and display any character
[pattern list] Match and display any character that matches one of the patterns inside the brackets. This is useful for accepting upper- and lower- case alphabetics, normally treated distinctly.
[Aa] Match and display any alphabetic character as is.

Any modifiers can be preceded by a number, which causes the modifier to try to match the specified number of characters. A special case is 0, which means "Match this pattern for as long as you can."

Here are some more examples:

{'HoTMetaL' FMT 'aAa'}

This displays hOt: the pattern matches three characters, the first and third to be displayed in lowercase and the second displayed in uppercase.

{'HoTMetaL' FMT '8a'}

This displays hotmetal: the pattern matches eight characters, and displays them in lowercase.

{'HoTMetaL' FMT '0a'}

This also displays hotmetal, but unlike the previous pattern, it will display the whole string in lowercase even if it has more than eight characters.

{'HoTM3taL' FMT '8a'}

Displays hotm; this pattern could match up to eight letters, but it has to stop after four characters because a number is present.

Try to understand the following patterns:

Expression Displays
{'HoTM3taL' FMT '0A#'} HOTM3
{'HoTM3taL' FMT '0a#0A'} hotm3TAL
{'HoTM3taL' FMT '6.'} HoTM3t
{'HoTM3taL' FMT '8[Aa]'} HoTM

Any of the pattern modifiers may also be preceded by an exclamation point, to invert the meaning of the format. If the pattern contains a number, the '!' must come before the number.

Pattern Function
!# Match any non-digit
!A Match any non-alpha.
!a Same as '!A'
!. Match and discard any character.

Here are some examples:

{'HoTM3taL' FMT '!0#'}

This will display HoTM; the pattern matches as many non-numeric characters as it can.

{'HoTM3taL' FMT '!0#!a'}

This will display HoTM3; the pattern matches as many non-numeric characters as it can, followed by a single non-letter.

{'HoTM3taL' FMT '3.!2.0.'}

This will display HoTtaL; the pattern matches 3 characters of any type, then matches and discards two characters of any type, then matches as many characters of any type as it can.

If you enclose two or more modifiers in square brackets, '[' and ']', the pattern thus created will match any character that any of the modifiers match. The most common example of this is '[aA]', which will match an uppercase or lowercase letter and display it unmodified. Here is another example:

{'HOTM3Tal' FMT '0[A#]'}

This will display HOTM3T.

In addition to displaying characters, a formatting pattern can also insert new characters. The expression &digits; with the ASCII code digits (where digits can be up to three characters). A frequent application of this is inserting currency symbols in numbers. For example:

{'15000' FMT '3#&36;2#'}
{'100' FMT '&163;0#'

These expressions will display 150$00 and £100 respectively. The ASCII code for '$' is 36 and the ASCII code for '£' is 163.

The '&digits;' expression cannot insert a character at the end of a string. On technique for working around this is to temporarily add a character to the string:

{('69' $ 'x') FMT '2.&36;'}

This will display 69$.

Numbers such as monetary amounts, telephone numbers, credit card numbers and dates are often displayed with separators in order to improve readability. For example, in the U.S. and in English Canada monetary amounts are usually displayed with a comma between every three digits. The FMT operator supports a notation for specifying such separators. Here is an example:

<MvEVAL EXPR="{'80550998' FMT '3+,0#'}">

This displays 80,550,998

The characters '3+,' in the pattern above cause FMT to insert the commas. In general, the notation n+c, where n is a number and c is a character means: "Insert character c every n characters, going from right to left."



Formatting U.S./English Canadian monetary values

Combining format patterns specifically for displaying numbers as dollars and cents according to the conventions used in the U.S. and in English Canada is easily accomplished using FMT.

Example Code:
<MvASSIGN NAME="usmoneyfmt" VALUE="&36;3+,0#.2P">
<MvASSIGN NAME="price" VALUE="569812.3">
<MvEVAL EXPR="{price FMT usmoneyfmt}">

This displays $569,812.30

The format expression above is interpreted as follows:

  1. The "&36;" says: "Prepend a "$" to the unformatted string."
  2. The "3+," says: "Insert a comma every three digits going from right to left, starting at the decimal."
  3. The "0#" says: "Any number of digits are allowed going from left to right." This is not affected by any characters inserted via the currently executing FMT pattern.
  4. The "." says: "Match any single character (including a period or decimal)." This is still reading left to right.
  5. The "2P" says; "Match a digit but put a "0" (zero) if there are no more digits to match." (P is a mnemonic for "Pad," and 2P is the same as PP)

If your site administrator has installed the format pattern usmoneyfmt into your system's site variables file, the pattern can be used as a variable in any Miva Script program.



System variables

System variables are automatically initialized when a Miva Script program begins execution. There are two types of system variables: static and dynamic. A static system variable has its value set only when the script begins execution, and dynamic system variables have their values set each time they are used in an expression. All of the time-related variables (except for dyn_time_remaining and globaltimeout) have both static and dynamic versions: the dynamic variables start with the dyn_ prefix. With the exception of recno, all other system variables are static.



Time variables

Dynamic Static Return value
dyn_time_t time_t Number of seconds since 1 Jan 1970 (numeric)
dyn_tm_hour tm_hour Hour in current day (numeric)
dyn_tm_isdst tm_isdst Has the value true (1) if daylight time is in effect in this timezone (boolean)
dyn_tm_mday tm_mday Day of the month (numeric)
dyn_tm_min tm_min Minutes in current hour (numeric)
dyn_stm_mon stm_mon Month of the year (string)
dyn_tm_mon tm_mon Month of the year (numeric)
dyn_tm_sec tm_sec Seconds in current minute (numeric)
dyn_tm_usec tm_usec The current microsecond, relative to dyn_tm_sec or tm_sec. (On Windows, this value is updated only in 50-millisecond increments).
dyn_stm_wday stm_wday Day of the week (string)
dyn_tm_wday tm_wday Day of the week (numeric)
dyn_tm_yday tm_yday Day in year (numeric)
dyn_tm_year tm_year Year (numeric)
dyn_stm_zone stm_zone The time zone (string)
globaltimeout Maximum total number of seconds that this script can execute.
dyn_time_remaining   Number of seconds before the current script will time out.

Note: The values returned by the dyn_tm_ variables are not zero-padded; for example, if the time is 12:04, dyn_tm_min returns '4', not '04'. You can use the padl() function to perform padding, if required.



CGI, HTTP, and other variables

All available CGI environment variables are automatically converted into static Miva Script system variables upon start-up. All HTTP headers are saved in environment variables and therefore are also converted to Miva Script static variables. The availability of environment variables depends on the server software; the availability of HTTP headers also depends on the browser; therefore, not all variables listed here are guaranteed to be available in all circumstances. For more information on HTTP headers and environment variables, consult a CGI reference and/or your server documentation. One reference that we recommend is The HTML 4.0 Sourcebook (http://www.wiley.com/compbooks/graham/html4ed/).


Note: HTTP servers convert HTTP headers into environment variables in which '-' is converted to '_', and to which the prefix 'HTTP' is added.

In the CGI version of Miva Empresa, all available HTTP headers and CGI variables (except for TERMCAP, MAIL, LOGNAME, and PATH) are accessible. In the NSAPI version, a selection of variables and headers (listed below) are accessible. With Miva Mia, all available headers are accessible, as are the variables remote_addr, remote_host, request_method, server_name, server_port, and server_protocol.

The list below contains many of the variables that you may encounter. If the browser and/or server software support other variables, they will be inherited by Miva Script where possible. The following key is used here to summarize the accessibility of system variables:

  • Origin:
    • S: generated by Miva Empresa, Miva Mia
    • E: if available, inherited from environment
    • H: if available, inherited from HTTP header (via the environment)
  • Platform:
    • C: accessible with CGI version of Miva Empresa
    • N: accessible with NSAPI version of Miva Empresa
    • P: accessible with Miva Mia

For example, E: C,N means that the variable in question is inherited from the environment and is accessible in the CGI and NSAPI versions of Miva Empresa.

Variable Return value Accessibility
apitype Platform: 'CGI', 'NSAPI', or 'Mia' (Miva Mia) S: C,N,P
argN If a list of values is passed to a Miva Script program, argN is the value of the Nth argument on the URL used to call the script. arg1 always contains the program file name. The first argument after the file name will be arg2, and so forth. S: C,N,P
auth_type Authentication method user by the server E: C,N
callerid Each time a cookie-enabled browser accesses a Miva Script document, Miva Script creates a 32-character cookie that is unique to that browser and URL. The cookie lasts for one year after being set. Cookies can be turned off in Miva Empresa; contact your server administrator. S: C,N,P
content_length Length of any attached (POST) information E: C
content_type Type of data for POST E: C
documenturl Contains URL of the currently running Miva Script program. This URL also contains the character between the program name and the command line arguments ('+' for NSAPI and Miva Mia, and '?' for CGI). S: C,N,P
gateway_interface Version of CGI used E: C
http_accept Comma-separated list of MIME types (type/subtype) that the browser will accept. This list is very incomplete on most browsers. H: C,N,P
http_accept_charset Character sets preferred by the browser (other than the default ASCII or ISO Latin-1) H: C,N,P
http_accept_language ISO codes for the languages preferred by the browser H: C,N,P
http_connection String that browser sends to the server to preserve a TCP connection (also called a "keep-alive" string). Not supported by all browsers and servers. H: C,N,P
http_cookie Contents of all the cookies set for the document. H: C,N,P
http_host Remote host name (usually same as server_name) H: C,N,P
http_pragma Mode client is running under H: C,P
http_referer The document that the current document was accessed from. H: C,P
http_user_agent Browser name, platform, version, and library H: C,N,P
mivaversion Version of the Miva Script language preprocessor S: C,N,P
nargs If a list of values is passed to a Miva Script program, nargs is the number of arguments on the URL, including the program file name. S: C,N,P
path_info Extra path information in the URL (a directory path that occurs immediately after the name of the CGI program in the URL) E: C,N
path_translated path_info, translated to a physical location by prepending the server document directory to its value E: C
process_id Currently running process number S: C,N,P
query_string Information passed after a URL and a "?" via the GET method E: C
recno Current record number in a flat file being accessed by <MvIMPORT>, or current record number in a database S: C,N,P
remote_addr IP address of remote host E: C,N,P
remote_host The domain name of the remote host E: C,N,P
remote_ident Remote user name, from servers that support RFC 931 identification E: C
remote_user User name associated with protected script, on servers that support user authentication E: C,N
request_method GET, POST, or HEAD E: C,N,P
script_name Virtual path to CGI script; not mapped locally to actual path E: C
server_hostname The name of the server E: C,N
server_name Same as server_hostname E: C,N,P
server_port Port under which server is running E: C,P
server_port_secure Whether the port is secure (boolean) E: C
server_protocol Name and revision of protocol used E: C,N,P
server_software HTTP server and version number that processed the request E: C,P
server_url server_hostname, in URL format E: C,N
server_version Version of the server E: C,N
user_agent Same as http_user_agent H: N
version Miva Empresa or Miva Mia version number S: C,N,P

Miva Script also allows site administrators to define their own system variables. Check with your administrator to find out whether any have been added to your site.


Note: Miva does not give scripts direct access to standard input (STDIN).



Built-in functions

Miva Script makes a set of built-in functions available to perform standard time oriented calculation, string formatting, string analysis, integer matching, number rounding, exponential multiplication, and file manipulation. Built-in functions tend to run faster than ordinary Miva Script code, so you should try to use of them whenever appropriate.

Calling built-in functions follows the same rules as calling user-defined functions: functions can and must appear in expressions only, and function arguments can consist any expression:

<MvEVAL EXPR="{fdelete(filename $ '.dat')}">
...
<MvIF EXPR="{asciivalue(char) GT 32}">


Time functions

Most of these functions must be called with two arguments, here designated time_t and time_zone. time_t is the number of seconds since the beginning of C.U.T. (Coordinated Universal Time) at 00:00:00 January 1, 1970. This value can be obtained from the system variables time_t and dyn_time_t (time_t is set when the script starts executing; dyn_time_t is updated at the moment that it is used; which one you use depends on what your program does). time_zone is the the number of hours before or after GMT; this value can be obtained using the built-in function timezone(). Here is an example of how a time function can be called:

<MvASSIGN NAME="my_time_zone" VALUE="{timezone()}">
<MvASSIGN NAME="now" VALUE="{time_t_sec(dyn_time_t,my_time_zone)}">

Note: These functions cannot be used to process dates earlier than January 1, 1970, or later than January 19, 2038.

FunctionName(parameters) Action
time_t_month(time_t, time_zone) Returns the current month as a number
time_t_year(time_t, time_zone) Returns the current year
time_t_hour(time_t, time_zone) Returns current hour (using a 24-hour clock)
time_t_min(time_t, time_zone) Returns the current minute in the hour
time_t_sec(time_t, time_zone) Returns the current second in the minute
time_t_dayofmonth(time_t, time_zone) Returns the current day of the month
time_t_dayofweek(time_t, time_zone) Returns the current day of the week as a number (Sunday=1)
time_t_dayofyear(time_t, time_zone) Returns the number of days since the beginning of the year, including today
timezone() Returns an integer which is the number of hours behind or ahead of GMT (not accounting for Daylight Time)
mktime_t(year, month, dayofmonth,
hours, minutes, seconds, time_zone)
Returns the time_t value for the time specified
makesessionid() Returns a 128-bit unique ID.

Tip: The following code will perform a "time delay", (also called a "wait" or "sleep") during which the program appears to do nothing. The value of the variable delay is the number of seconds of delay time.
<MvASSIGN NAME="delay" VALUE="10">
<MvASSIGN NAME="stop" VALUE="{dyn_time_t+delay}">
<MvWHILE EXPR="{dyn_time_t LE stop}"
<MvWHILE>



Text string functions

Note: literal strings or characters used as arguments to these functions must be surrounded by single quotes, '...'. For example: isalpha('r2d2'). These functions do not modify their arguments; they return values based on those arguments.

Boolean-valued string functions

These functions all start with is (for example, isalpha(), isdigit) and return a true (1) or false (0) value depending on the composition of the string. Each of these functions is based on the C language function of the same name, but is applied to the whole string: isdigit(string) will return true if every character in the string is a digit, and false otherwise.


Note: For all functions except isdigit() and isxdigit(), the set of characters understood to be alphabetic is inherited from the setlocale() setting used on the machine running Miva. For this reason, these functions are not guaranteed to return the same results on all machines.

Function Name(parameters) Action
isalnum(string) Returns true (1) if all characters in string are either alphabetic or digits, and false (0) otherwise.
isalpha(string) Returns true (1) if all characters in string are alphabetic and false (0) otherwise.
isascii(string) Returns true (1) if all characters in string are ASCII characters (those with decimal value between 0 and 127), and false (0) otherwise.
iscntrl(string) Returns true (1) if all characters in string are control characters (those with decimal value between 0 and 31, or 127), and false (0) otherwise.
isdigit(string) Returns true (1) if all characters in string are digits in the range 0-9, and false (0) otherwise.
isgraph(string) Returns true (1) if all characters in string are graphic characters (those with decimal value between 33 and 127), and false (0) otherwise.
islower(string) Returns true (1) if all characters in string are lowercase letters, and false (0) otherwise.
isprint(string) Returns true (1) if all characters in string are printable characters (same as graphic characters, with the addition of the space character), and false (0) otherwise.
ispunct(string) Returns true (1) if all characters in string are punctuation characters (non-alphanumeric graphics characters), and false (0) otherwise.
isspace(string) Returns true (1) if all characters in string are whitespace (space, tab, vertical tab, newline, form feed) characters, and false (0) otherwise.
isupper(string) Returns true (1) if all characters in string are uppercase letters, and false (0) otherwise.
isxdigit(string) Returns true (1) if all characters in string are hexadecimal digits (a-f, A-F, 0-9), and false (0) otherwise.




Other string functions

Function Name(parameters) Action
asciichar(number) Returns the character corresponding to number (number must be less than 255). See the Note below.
asciivalue(character) Returns the ASCII numeric value for character (character must be a single character).
decodeattribute(string) Returns a copy of string (which is usually a URL) converted from URL-encoded format to ordinary text. This function is the opposite of encodeattribute.
decodeentities(string) Returns a copy of string in which all HTML entities have been converted to their plain text equivalents (for example, '&lt;' is converted to '<'). This function is the opposite of encodeentities.
encodeattribute(string) Returns a copy of string (which is usually a URL) in URL-encoded format. Special characters such as space, tilde (~), and the plus sign are converted to hexadecimal %nn format. This function is the opposite of decodeattribute.
encodeentities(string) Returns a copy of string in which all characters have been converted to their HTML entity equivalents, where applicable (for example, '<' is converted to '&lt;'). This function is the opposite of decodeentities. See http://www.w3.org/TR/REC-html40/sgml/entities.html for a list of HTML entities.
gettoken(string, separators, n) Tokenizes string, using any of the characters in separators as token separators, and returns the nth token. A null string is returned if there is no nth token.
glosub(string, search, replace) Global substitution; returns a copy of string in which all instances of string search have been replaced by string replace. (Note: to represent a backslash (\) in replace, use '\\').
len(string) Returns the number of characters in string.
ltrim(string) Returns a copy of string with all space characters removed from the left end.
miva_getvarlist(scope) Returns a comma-separated list of the names of all currently defined variables with the given scope. scope must be a literal string: 'l', 'local', 'g', 'global', 's', 'system'. ( Note: For xBase3 databases, use <MvREVEALSTRUCTURE> )
padl(string, length, padcharacter) Returns a string length characters long, consisting of string padded on the left with as many instances of padcharacter as are needed to make up the full length.
padr( string, length, padcharacter) Returns a string length characters long, consisting of string padded on the right with as many instances of padcharacter as are needed to make up the full length.
rtrim(string) Returns a copy of string with all space characters removed from the right end.
substring(string, start, length) Returns the substring of stringbeginning at position start, length characters long.
tolower(string) Returns a copy of string in lower case.
toupper(string) Returns a copy of string in upper case.

Note: The asciichar(n) function puts out a single byte whose value is n. It is up to the client application (such as a browser) to render this value as a character. Almost all HTML browsers support the ISO Latin-1 character encoding, and will display asciichar(n) as the character whose ISO Latin-1 (decimal) encoding is n. The rendering of non-printable characters, and values of asciichar(n) where n is not associated with a character in ISO Latin-1, is undefined. In particular, the rendering of asciichar(n) where n is in the decimal range 129-160, is undefined, though many browsers will display the corresponding ANSI character. The rendering of the output of asciichar(n) by an arbitrary application follows the character encoding used by that application, and is in general platform-specific.



Numerical functions


Note: The arguments of the trigonometric functions sin, cos, tan, sinh, cosh, and tanh should be expressed in radians; the results of asin, acos, atan, and atan2