Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

3366 lines (2298 sloc) 131.788 kB
!init OPT_LOOK="icdevgroup"; OPT_STYLE="manual"
# $Id: ictemplates.sdf,v 1.53 2004-05-05 15:22:30 jon Exp $
!define DOC_NAME "Template Guide"
!define DOC_TYPE ""
!define DOC_CODE "ictemplates"
!define DOC_VERSION substr('$Revision: 1.53 $',11, -2)
!define DOC_STATUS "Draft"
!define DOC_PROJECT "Interchange"
!define DOC_URL "http://www.icdevgroup.org/doc/ictemplates.html"
!build_title
H1: Introduction
Interchange is designed to build its pages based on templates from a database. This document describes how to build templates using the Interchange Tag Language (ITL) and explains the different options you can use in a template.
H2: Overview
The search builder can be used to generate very complex reports on the database or to help in the construction of ITL templates. Select a "Base table" that will be the foundation for the report. Specify the maximum number of rows to be returned at one time, and whether to show only unique entries.
The "Search filter" narrows down the list of rows returned by matching table columns based on various criteria. Up to three separate conditions can be specified. The returned rows must match all criteria.
Finally, select any sorting options desired for displaying the results and narrow down the list of columns returned if desired. Clicking "Run" will run the search immediately and display the results. "Generate definition" will display an ITL tag that can be placed in a template and that will return the results when executed.
To build complex order forms and reports, Interchange has a complete tag language with over 80 different functions called Interchange Tag Language (ITL). It allows access to and control over any of an unlimited number of database tables, multiple shopping carts, user name/address information, discount, tax and shipping information, search of files and databases, and much more.
There is some limited conditional capability with the
E<lbracket>{{C[jump="ictags.html#if"]if}}] tag,
but when doing complex operations, use of embedded Perl/ASP should be strongly considered. Most of the tests use Perl code, but Interchange uses the Safe.pm module with its default restrictions to help ensure that improper code will not crash the server or modify the wrong data.
Perl can also be embedded within the page and, if given the proper permission by the system administrator, call upon resources from other computers and networks.
H1: About Variable Replacement
Variable substitution is a simple and often used feature of Interchange templates. It allows you to set a variable to a particular value in the {{FILE:catalog.cfg}} directory. Then, by placing that variable name on a page, you invoke that value to be used. Before anything else is done on a template, all variable tokens are replaced by variable values. There are three types of variable tokens:
C<__VARIABLENAME__> is replaced by the catalog variable called VARIABLENAME.
C<@@VARIABLENAME@@> is replaced by the global variable called VARIABLENAME.
C<@_VARIABLENAME_@> is replaced by the catalog variable VARIABLENAME if it exists; otherwise, it is replaced by the global variable VARIABLENAME.
For more information on how to use the C<Variable> configuration file directive to set global variables in C<interchange.cfg> and catalog variables in C<catalog.cfg>, see the {{1:Interchange Configuration Guide}}.
H1: Using Interchange Template Tags
This section describes the different template specific tags and functions that are used when building a your templates.
H2: Understanding Tag Syntax
Interchange uses a style similar to HTML, but with [square brackets] replacing
<chevrons>. The parameters that can be passed are similar, where
a parameter="parameter value" can be passed.
\Summary:
!block example
[tag parameter] Tag called with positional parameter
[tag parameter=value] Tag called with named parameter
[tag parameter="the value"] Tag called with space in parameter
[tag 1 2 3] Tag called with multiple positional parameters
[tag foo=1 bar=2 baz=3] Tag called with multiple named parameters
[tag foo=`2 + 2`] Tag called with calculated parameter
[tag foo="[value bar]"] Tag called with tag inside parameter
[tag foo="[value bar]"]
Container text. Container tag.
[/tag]
!endblock
Most tags can accept some positional parameters. This makes parsing faster and
is, in most cases, simpler to write.
The following is an example tag:
!block example
E<lbracket>{{C[jump="ictags.html#value"]value}}C< name=city>]
!endblock
This tag causes Interchange to look in the user form value array and return
the value of the form parameter C<city>, which might have been set with:
!block example
City: <INPUT TYPE=text NAME=city VALUE="[value city]">
!endblock
Note: Keep in mind that the value was pre-set with the value of C<city> (if any).
It uses the positional style, meaning C<name> is the first positional parameter
for the [value ...] tag. Positional parameters cannot be derived from other Interchange tags. For example, [value [value formfield]] will not work. But, if the
named parameter syntax is used, parameters can contain other tags. For example:
!block example
E<lbracket>{{C[jump="ictags.html#value"]value}}C< name="[value formfield]"]>]
!endblock
There are exceptions to the above rule when using list tags such as
E<lbracket>{{C[jump="ictags.html#item-list"]item-list}}],
E<lbracket>{{C[jump="ictags.html#loop"]loop}}],
E<lbracket>{{C[jump="ictags.html#query"]query}}] and others.
These tags, and their exceptions, are explained in their corresponding sections.
Many Interchange tags are container tags. For example:
!block example
E<lbracket>{{C[jump="ictags.html#set"]set}} Checkout]
mv_nextpage=ord/checkout
mv_todo=return
[/set]
!endblock
Tags and parameter names are not case sensitive, so C<[VALUE NAME=something]>
and C<[value name=something]> work the same.
The Interchange development convention is to type HTML tags in upper
case and Interchange tags in lower case. This makes pages and tags easier to read.
Single quotes work the same as double quotes and can prevent confusion. For example:
!block example
[value name=b_city set='[value city]']
!endblock
Backticks should be used with extreme caution since they cause the parameter contents to be evaluated
as Perl code using the E<lbracket>{{C[jump="ictags.html#calc"]calc}}]
tag. For example:
!block example
[value name=row_value set=`$row_value += 1`]
!endblock
is the same as
!block example
[value name=row_value set="E<lbracket>{{C[jump="ictags.html#calc"]calc}}]$row_value += 1[/calc]"]
!endblock
Vertical bars can also be used as quoting characters, but have the unique behavior of stripping leading and trailing whitespace. For example:
!block example
[loop list="
k1 A1 A2 A3
k2 B1 B2 B3"]
[loop-increment][loop-code]
[/loop]
!endblock
could be better expressed as:
!block example
[loop list=|
k1 A1 A2 A3
k2 B1 B2 B3
|]
[loop-increment][loop-code]
[/loop]
!endblock
How the result of the tag is displayed depends on if it is a container or a standalone tag. A container tag has a closing tag (for example, C<[tag] stuff [/tag]>). A standalone tag has no end tag (for example,
E<lbracket>{{C[jump="ictags.html#area"]area}}C< href=somepage>].
Note that
E<lbracket>{{C[jump="ictags.html#page"]page}}] and
E<lbracket>{{C[jump="ictags.html#order"]order}}]
are B<not> container tags. (C<[/page]> and C<[/order]> are simple macros.)
A container tag will have its output re-parsed for more Interchange tags by default. To inhibit this behavior, set the attribute reparse to 0. However, it has been found that the default re-parsing is almost always desirable. On the other hand, the output of a standalone tag will not be re-interpreted for Interchange tag constructs (with some exceptions,
like E<lbracket>{{C[jump="ictags.html#include"]include}}C< file>].
Most container tags will not have their contents interpreted (Interchange tags parsed) before being passed the container text. Exceptions include
E<lbracket>{{C[jump="ictags.html#calc"]calc}}],
E<lbracket>{{C[jump="ictags.html#currency"]currency}}] and
E<lbracket>{{C[jump="ictags.html#seti"]seti}}].
All tags accept the C<interpolate=1> tag modifier, which causes the interpretation to take place.
H2: data and field
The E<lbracket>{{C[jump="ictags.html#data"]data}}] and
E<lbracket>{{C[jump="ictags.html#field"]field}}]
tags access elements of Interchange databases. They are the form used outside of the iterating lists, and are used to do lookups when the table, column/field or key/row is conditional based on a previous operation.
The following are equivalent for attribute names:
!block example
table --> base
col --> field --> column
key --> code --> row
!endblock
The E<lbracket>{{C[jump="ictags.html#field"]field}}]
tag looks in any tables defined as C<ProductFiles>, in that order, for the data and returns the first non-empty value. In most catalogs, where C<ProductFiles> is not defined, i.e., the demo, C<[field title 00-0011]> is equivalent to C<[data products title 00-0011]>. For example, [field col=foo key=bar] will not display something from the table "category" because "category" is not in the directive ProductFiles or there are multiple ProductFiles and an earlier one has an entry for that key.
LI1: E<lbracket>{{C[jump="ictags.html#data"]data}}C< table column key>]
.named attributes: C<[data base="database" field="field" key="key"> C<value="value" op="increment]>
.Returns the value of the field in any of the arbitrary databases or from the variable namespaces. If the option C<increment=1> is present, the field will be automatically incremented with the value in value.
.If a DBM-based database is to be modified, it must be flagged writable on the page calling the write tag. For example, use C<[tag flag write]products[/tag]> to mark the C<products> database writable.
.In addition, the C<[data ...]> tag can access a number of elements in the Interchange session database:
!block example; listitem=2
accesses Accesses within the last 30 seconds
arg The argument passed in a [page ...] or [area ...] tag
browser The user browser string
host Interchange's idea of the host (modified by DomainTail)
last_error The last error from the error logging
last_url The current Interchange path_info
logged_in Whether the user is logged in via UserDB
pageCount Number of unique URLs generated
prev_url The previous path_info
referer HTTP_REFERER string
ship_message The last error messages from shipping
source Source of original entry to Interchange
time Time (seconds since Jan 1, 1970) of last access
user The REMOTE_USER string
username User name logged in as (UserDB)
!endblock
.Databases will hide variables, so if a database is named "session," "scratch," or any of the other reserved names it won't be able to use the C<[data ...]> tag to read them. Case is sensitive, so the database could be called "Session," but this is not recommended practice.
LI1: E<lbracket>{{C[jump="ictags.html#field"]field}}C< name code>]
.named attributes: C<[field code="code" name="fieldname"]>
.Expands into the value of the field name for the product as identified by code found by searching the products database. It will return the first entry found in the series of Product Files in the products database. If this needs to constrained to a particular table, use a C<[data table col key]> call.
H2: set, seti, tmp, tmpn scratch and scratchd
Scratch variables are maintained in the user session, which is separate from the form variable values set on HTML forms. Many things can be controlled with scratch variables, particularly search and order processing, the C<mv_click> multiple variable setting facility and key Interchange conditions session URL display.
There are four tags that are used to set the scratch space:
E<lbracket>{{C[jump="ictags.html#set"]set}}C< variable>] value [/set],
E<lbracket>{{C[jump="ictags.html#seti"]seti}}C< variable>] value [/seti],
E<lbracket>{{C[jump="ictags.html#tmp"]tmp}}C< variable>] value [/tmp],
E<lbracket>{{C[jump="ictags.html#tmpn"]tmpn}}C< variable>] value [/tmpn] and
and two tags for reading scratch space:
E<lbracket>{{C[jump="ictags.html#scratch"]scratch}}C< variable>] and
E<lbracket>{{C[jump="ictags.html#scratchd"]scratchd}}C< variable>].
LI1: E<lbracket>{{C[jump="ictags.html#set"]set}}C< variable>] value [/set]
.Sets a scratchpad variable to a value.
.Most of the mv_* variables that are used for search and order conditionals are in another namespace. They can be set through hidden fields in a form.
.An order profile would be set with:
!block example; listitem=2
[set checkout]
name=required Please enter your name.
address=required No address entered.
[/set]
<INPUT TYPE=hidden NAME=mv_order_profile VALUE="checkout">
!endblock
.A search profile would be set with:
!block example; listitem=2
[set substring_case]
mv_substring_match=yes
mv_case=yes
[/set]
<INPUT TYPE=hidden NAME=mv_profile VALUE="substring_case">
!endblock
.To do the same as C<[set foo]bar[/set]> in embedded Perl:
!block example; listitem=2
[calc]$Scratch->{foo} = 'bar'; return;[/calc]
!endblock
LI1: E<lbracket>{{C[jump="ictags.html#seti"]seti}}C< variable>] value [/seti]
.The same as E<lbracket>{{C[jump="ictags.html#set"]set}}]
except it interpolates the container text. The above is the same as:
!block example; listitem=2
E<lbracket>{{C[jump="ictags.html#set"]set}} name=variable interpolate=1] E<lbracket>{{C[jump="ictags.html#value"]value}} something] [/set]
!endblock
LI1: E<lbracket>{{C[jump="ictags.html#tmp"]tmp}}C< variable>] value [/tmp]
.The same as E<lbracket>{{C[jump="ictags.html#seti"]seti}}]
except it does not persist.
LI1: E<lbracket>{{C[jump="ictags.html#tmpn"]tmpn}}C< variable>] value [/tmpn]
.The same as E<lbracket>{{C[jump="ictags.html#set"]set}}]
except it does not persist.
LI1: E<lbracket>{{C[jump="ictags.html#scratch"]scratch}}C< variable>]
.Returns the contents of a scratch variable to the page. C<[scratch foo]> is the same as, but faster than:
!block example; listitem=2
E<lbracket>{{C[jump="ictags.html#perl"]perl}}] $Scratch->{foo} [/perl]
!endblock
LI1: E<lbracket>{{C[jump="ictags.html#scratchd"]scratchd}}C< variable>]
.The same as E<lbracket>{{C[jump="ictags.html#scratch"]scratch}}C< variable>]
except it deletes the value after returning it. Same as C<[scratch foo] [{{C[jump="ictags.html#set"]set}} foo] [/set]>.
LI1: E<lbracket>{{C[jump="ictags.html#if"]if}}C< scratch name op* compare*]> yes C<[else]> no C<[/else][/if]>
.Tests a scratch variable. See the E<lbracket>{{C[jump="ictags.html#if"]if}}]
tag documentation for more information.
H2: loop
Loop lists can be used to construct arbitrary lists based on the contents of a database field, a search or other value (like a fixed list). Loop accepts a C<search> parameter that will do one-click searches on a database table (or file).
To iterate over all keys in a table, use the idiom (C<[loop search="ra=yes/ml=9999"] [/loop]>. C<ra=yes> sets C<mv_return_all>, which means "match everything". C<ml=9999> limits matches to that many records. If the text file for searching an Interchange DBM database is not used, set C<st=db> (mv_searchtype).
When using C<st=db>, returned keys may be affected by C<TableRestrict>. Both can be sorted with C<[sort table:field:mod -start +number]> modifiers. See {{C[jump="icdatabase.html#Sorting"]sorting}}.
The Interchange Tags Reference has more information on the
E<lbracket>{{C[jump="ictags.html#loop"]loop}}] tag.
LI1: E<lbracket>{{C[jump="ictags.html#loop"]loop}}C< item item item]> LIST C<[/loop]>
.named attributes: C<[loop prefix=label* list="item item item"* search="se=whatever"*]>
.Returns a string consisting of the LIST, repeated for every item in a comma-separated or space-separated list. This tag works the same way as the C<[item-list]> tag, except for order-item-specific values. It is intended to pull multiple attributes from an item modifier, but can be useful for other things, like building a pre-ordained product list on a page.
.Loop lists can be nested by using different prefixes:
!block example; listitem=2
[loop prefix=size list="Small Medium Large"]
[loop prefix=color list="Red White Blue"]
[color-code]-[size-code]<BR>
[/loop]
<P>
[/loop]
!endblock
.This will output:
!block example; listitem=2
Red-Small
White-Small
Blue-Small
Red-Medium
White-Medium
Blue-Medium
Red-Large
White-Large
Blue-Large
!endblock
.The search="args" parameter will return an arbitrary search, just as in a one-click search:
!block example; listitem=2
[loop search="se=Americana/sf=category"]
[loop-code] [loop-field title]
[/loop]
!endblock
.The above will show all items with a category containing the whole world "Americana."
LI1: E<lbracket>{{C[jump="ictags.html#if-PREFIX-data"]if-loop-data}}C< table field]> IF C<[else]> ELSE C<[/else][/if-loop-data]>
.Outputs the IF if the C<field> in the C<table> is not empty or the ELSE (if any) otherwise.
Note: This tag does not nest with other C<if-loop-data> tags.
LI1: E<lbracket>{{C[jump="ictags.html#if-PREFIX-field"]if-loop-field}}] IF C<[else]> ELSE C<[/else][/if-loop-field]>
.Outputs the IF if the C<field> in the C<products> table is not empty or the ELSE (if any) otherwise.
Note: This tag does not nest with other C<if-loop-field> tags.
LI1: [loop-alternate N] DIVISIBLE [else] NOT DIVISIBLE [/else][/loop-alternate]
.Set up an alternation sequence. If the loop-increment is divisible by C<N>, the text will be displayed. If C<[else]NOT DIVISIBLE TEXT [/else]> is present, then the NOT DIVISIBLE TEXT will be displayed. For example:
!block example; listitem=2
[loop-alternate 2]EVEN[else]ODD[/else][/loop-alternate]
[loop-alternate 3]BY 3[else]NOT by 3[/else][/loop-alternate]
!endblock
LI1: [/loop-alternate]
.Terminates the alternation area.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-change"]loop-change}}C< marker>]
.Same as C<[item-change]>, but within loop lists.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-code"]loop-code}}]
.Evaluates to the first returned parameter for the current returned record.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-data"]loop-data}}C< database fieldname>]
.Evaluates to the field name fieldname in the arbitrary database table database for the current item.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-description"]loop-description}}]
.Evaluates to the product description for the current item. Returns the <Description Field> from the first products database where that item exists.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-field"]loop-field}}C< fieldname>]
.The E<lbracket>{{C[jump="ictags.html#PREFIX-field"]loop-field}}]
.The E<lbracket>{{C[jump="ictags.html#PREFIX-field"]loop-field}}] tag is special in that it looks in any of the tables defined as C<ProductFiles>, in that order, for the data and returns the value only if that key is defined. In most catalogs, where C<ProductFiles> is not defined C<[loop-field title]> is equivalent to
E<lbracket>{{C[jump="ictags.html#PREFIX-data"]loop-field}}C< products title>].
.Evaluates to the field name fieldname in the database for the current item.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-increment"]loop-increment}}]
.Evaluates to the number of the item in the list. Used for numbering items in the list. Starts from one (1).
LI1: [loop-last]tags[/loop-last]
.Evaluates the output of the ITL tags encased in the [loop-last] tags. If it evaluates to a numerical non-zero number (for example, 1, 23, -10 etc.), the loop iteration will terminate. If the evaluated number is negative, the item itself will be skipped. If the evaluated number is positive, the item itself will be shown, but will be last on the list.
!block example; listitem=2
[loop-last][calc]
return -1 if '[loop-field weight]' eq '';
return 1 if '[loop-field weight]' < 1;
return 0;
[/calc][/loop-last]
!endblock
.If this is contained in your C<[loop list]> and the weight field is empty, a numerical C<-1> will be output from the C<[calc][/calc]> tags; the list will end and the item will B<not> be shown. If the product's weight field is less than 1, a numerical 1 is output. The item will be shown, but it will be the last item on the list.
LI1: [loop-next]tags[/loop-next]
.Evaluates the output of the ITL tags encased in the [loop-next] tags. If it evaluates to a numerical non-zero number (for example, 1, 23, -10 etc.), the loop will be skipped with no output. Example:
!block example; listitem=2
[loop-next][calc][loop-field weight] < 1[/calc][/loop-next]
!endblock
.If this is contained in your C<[loop list]> and the product's weight field is less than 1, a numerical C<1> will be output from the C<[calc][/calc]> operation. The item will not be shown.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-price"]loop-price}}C< n* noformat*>]
.Evaluates to the price for the optional quantity n (from the products file) of the current item, with currency formatting. If the optional "noformat" is set, then currency formatting will not be applied.
LI1: E<lbracket>{{C[jump="ictags.html#PREFIX-calc"]loop-calc}}] PERL C<[/loop-calc]>
.Calls embedded Perl with the code in the container. All C<[loop-*]> tags can be placed inside except for C<[loop-filter ...][/loop-filter]>, C<[loop-exec routine][/loop-exec]>, C<[loop-last][/loop-last]> and C<[loop-next][/loop-next]>.
Note: All normal embedded Perl operations can be used, but be careful to pre-open any database tables with a [perl tables="tables you need"][/perl] tag prior to the opening of the [loop].
LI1: [loop-exec routine]argument[/loop-exec]
.Calls a subroutine predefined either in catalog.cfg with Sub or in a
E<lbracket>{{C[jump="ictags.html#loop"]loop}}]
with C<[loop-sub routine]> PERL C<[/loop-sub]>. The container text is passed as C<$_[0]> and the array (or hash) value of the current row is C<$_[1]>.
LI1: [loop-sub routine]PERL[/loop-sub]
.Defines a subroutine that is available to the current (and subsequent) C<[loop-*]> tags within the same page. See {{CMD[jump="#Programming"]Interchange Programming}}.
H2: if
LI1: E<lbracket>{{C[jump="ictags.html#if"]if}}C< type field op* compare*>]
The Interchange Tags Reference has more information on the
E<lbracket>{{C[jump="ictags.html#if"]if}}] tag.
.named attributes: C<[if type="type" term="field" op="op" compare="compare"]>
LI1: [if !type field op* compare*]
.named attributes: C<[if type="!type" term="field" op="op" compare="compare"]>
Allows the conditional building of HTML based on the setting of various Interchange
session and database values. The general form is:
!block example; listitem=1
[if type term op compare]
[then]
If true, this text is printed on the document.
The [then] [/then] is optional in most
cases. If ! is prepended to the type
setting, the sense is reversed and
this text will be output for a false condition.
[/then]
[elsif type term op compare]
Optional, tested when if fails.
[/elsif]
[else]
Optional, printed on the document when all above fail.
[/else]
[/if]
!endblock
The E<lbracket>{{C[jump="ictags.html#if"]if}}] tag can also have some variants:
!block example; listitem=1
[if explicit]
[condition] CODE [/condition]
Displayed if valid Perl CODE returns a true value.
[/if]
!endblock
Some Perl-style regular expressions can be written:
!block example; listitem=1
[if value name =~ /^mike/i]
This is the if with Mike.
[elsif value name =~ /^sally/i]
This is an elsif with Sally.
[/elsif]
[elsif value name =~ /^barb/i]
[or value name =~ /^mary/i]
This is an elsif with Barb or Mary.
[elsif value name =~ /^pat/i]
[and value othername =~ /^mike/i]
This is an elsif with Pat and Mike.
[/elsif]
[else]
This is the else, no name I know.
[/else]
[/if]
!endblock
While the named parameter tag syntax works for C<[if ...]>, it is more convenient to use the positional syntax in most cases. The only exception is when you are planning to do a test on the results of another tag sequence:
This will not work:
!block example
[if value name =~ /[value b_name]/]
Shipping name matches billing name.
[/if]
!endblock
Do this instead:
!block example
[if type=value term=name op="=~" compare="/[value b_name]/"]
Shipping name matches billing name.
[/if]
!endblock
As an alternative:
!block example
[if type=value term=high_water op="<" compare="[shipping noformat=1]"]
The shipping cost is too high, charter a truck.
[/if]
!endblock
There are many test targets available. The following is a list of some of the available test targets.
LI1: config Directive
.The Interchange configuration variables. These are set by the directives in the Interchange configuration file.
!block example; listitem=2
[if config CreditCardAuto]
Auto credit card validation is enabled.
[/if]
!endblock
LI1: data database::field::key
.The Interchange databases. Retrieves a field in the database and returns true or false based on the value.
!block example; listitem=2
[if data products::size::99-102]
There is size information.
[else]
No size information.
[/else]
[/if]
[if data products::size::99-102 =~ /small/i]
There is a small size available.
[else]
No small size available.
[/else]
[/if]
!endblock
.If another tag is needed to select the key, and it is not a looping tag construct, named parameters must be used:
!block example; listitem=2
[set code]99-102[/set]
[if type=data term="products::size::[scratch code]"]
There is size information.
[else]
No size information.
[/else]
[/if]
!endblock
LI1: discount
.Checks to see if a discount is present for an item.
!block example; listitem=2
[if discount 99-102]
This item is discounted.
[/if]
!endblock
LI1: explicit
.A test for an explicit value. If Perl code is placed between a C<[condition]> C<[/condition]> tag pair, it will be used to make the comparison. Arguments can be passed to import data from user space, just as with the
E<lbracket>{{C[jump="ictags.html#perl"]perl}}] tag.
!block example; listitem=2
[if explicit]
[condition]
$country = $ values =~{country};
return 1 if $country =~ /u\.?s\.?a?/i;
return 0;
[/condition]
You have indicated a US address.
[else]
You have indicated a non-US address.
[/else]
[/if]
!endblock
.The same thing could be accomplished with C<[if value country =~ /u\.?s\.?a?/i]>, but there are many situations where this example could be useful.
LI1: file
.Tests for the existence of a file. This is useful for placing image tags only if the image is present.
!block example; listitem=2
[if file /home/user/www/images/[item-code].gif]
<IMG SRC="[item-code].gif">
[/if]
or
[if type=file term="/home/user/www/images/[item-code].gif"]
<IMG SRC="[item-code].gif">
[/if]
!endblock
.The C<file> test requires that the SafeUntrap directive contain C<ftfile> (which is the default).
LI1: items
.The Interchange shopping carts. If not specified, the cart used is the main cart. This is usually used to test to see if anything is in the cart. For example:
!block example; listitem=2
[if items]You have items in your shopping cart.[/if]
[if items layaway]You have items on layaway.[/if]
!endblock
LI1: ordered
.Order status of individual items in the Interchange shopping carts. Unless otherwise specified, the cart used is the main cart. The following items refer to a part number of 99-102.
!block example; listitem=2
{{2:[if ordered 99-102] ... [/if]}}
Checks the status of an item on order, true if item
99-102 is in the main cart.
{{2:[if ordered 99-102 layaway] ... [/if]}}
Checks the status of an item on order, true if item
99-102 is in the layaway cart.
{{2:[if ordered 99-102 main size] ... [/if]}}
Checks the status of an item on order in the main cart,
true if it has a size attribute.
{{2:[if ordered 99-102 main size =~ /large/i] ... [/if]}}
Checks the status of an item on order in the main cart,
true if it has a size attribute containing 'large'.
THE CART NAME IS REQUIRED IN THE OLD SYNTAX. The new
syntax for that one would be:
[if type=ordered term="99-102" compare="size =~ /large/i"]
To make sure it is the size that is large and not another attribute,
you could use:
[if ordered 99-102 main size eq 'large'] ... [/if]
{{2:[if ordered 99-102 main lines] ... [/if]}}
Special case -- counts the lines that the item code is
present on. (Only useful, of course, when mv_separate_items
or SeparateItems is defined.)
!endblock
LI1: scratch
.The Interchange scratchpad variables, which can be set with the C<[set name] value [/set]> element.
!block example; listitem=2
[if scratch mv_separate_items]
Ordered items will be placed on a separate line.
[else]
Ordered items will be placed on the same line.
[/else]
[/if]
!endblock
LI1: session
.The Interchange session variables. Of particular interest are logged_in, source, browser and username.
LI1: validcc
.A special case, it takes the form C<[if validcc no type exp_date]>. Evaluates to true if the supplied credit card number, type of card and expiration date pass a validity test. It performs a LUHN-10 calculation to weed out typos or phony card numbers.
LI1: value
.The Interchange user variables, typically set in search, control or order forms. Variables beginning with C<mv_> are Interchange special values and should be tested and used with caution.
=item variable
See Interchange I<Variable> values.
=back
The field term is the specifier for that area. For example, C<[if session frames]> would return true if the C<frames> session parameter was set.
As an example, consider buttonbars for frame-based setups. You might decide to display a different buttonbar with no frame targets for sessions that are not using frames:
!block example
[if session frames]
[buttonbar 1]
[else]
[buttonbar 2]
[/else]
[/if]
!endblock
Another example might be the when search matches are displayed. If using the string C<[value mv_match_count] titles found>, it will display a plural result even if there is only one match. Use:
!block example
[if value mv_match_count != 1]
[value mv_match_count] matches found.
[else]
Only one match was found.
[/else]
[/if]
!endblock
The op term is the compare operation to be used. Compare operations are the same as they are in Perl:
!block example
== numeric equivalence
eq string equivalence
> numeric greater-than
gt string greater-than
< numeric less-than
lt string less-than
!= numeric non-equivalence
ne string equivalence
!endblock
Any simple Perl test can be used, including some limited regex matching. More complex tests should be done with C<[if explicit]>.
LI1: [then] text [/then]
.This is optional if not nesting "if" conditions. The text immediately following the C<[if ..]> tag is used as the conditionally substituted text. If nesting C<[if ...]> tags, use C<[then][/then]> on any outside conditions to ensure proper interpolation.
LI1: [elsif type field op* compare*]
.named attributes: C<[elsif type="type" term="field" op="op" compare="compare"]>
.Additional conditions for test, applied if the initial C<[if ..]> test fails.
LI1: [else] text [/else]
.The optional else-text for an if or if-item-field conditional.
LI1: [condition] text [/condition]
.Only used with the C<[if explicit]> tag. Allows an arbitrary expression B<in Perl> to be placed inside, with its return value interpreted as the result of the test. If arguments are added to C<[if explicit args]>, those will be passed as arguments in the
E<lbracket>{{C[jump="ictags.html#perl"]perl}}] construct.
LI1: [/if]
.Terminates an if conditional.
H1: Programming
Interchange has a powerful paradigm for extending and enhancing its functionality. It uses two mechanisms, user-defined tags and user subroutines on two different security levels, global and catalog. In addition, embedded Perl code can be used to build functionality into pages.
User-defined tags are defined with the UserTag directive in either C<interchange.cfg> or C<catalog.cfg>. The tags in C<interchange.cfg> are global and they are not constrained by the C<Safe> Perl module as to which opcodes and routines they may use. The user-defined tags in C<catalog.cfg> are constrained by C<Safe>. However, if the C<AllowGlobal> global directive is set for the particular catalog in use, its UserTag and C<Sub> definitions will have global capability.
H2: Overriding Interchange Routines
Many of the internal Interchange routines can be accessed by programmers who can read the source and find entry points. Also, many internal Interchange routines can be overridden:
!block example
GlobalSub <<EOS
sub just_for_overriding {
package Vend::Module;
use MyModule;
sub to_override {
&MyModule::do_something_funky($Values->{my_variable});
}
}
EOS
!endblock
The effect of the above code is to override the C<to_override> routine in the module C<Vend::Module>. This is preferable to hacking the code for functionality changes that are not expected to change frequently. In most cases, updating the Interchange code will not affect the overridden code.
Note:Internal entry points are not guaranteed to exist in future versions of Interchange.
H2: Embedding Perl Code
Perl code can be directly embedded in Interchange pages. The code is specified as:
!block example
[perl]
$name = $Values->{name};
$browser = $Session->{browser};
return "Hi, $name! How do you like your $browser?";
[/perl]
!endblock
ASP syntax can be used with:
!block example
[mvasp]
<%
$name = $Values->{name};
$browser = $Session->{browser};
%>
Hi, <%= $name %>!
<%
HTML "How do you like your $browser?";
%>
[/mvasp]
!endblock
The two examples above are essentially equivalent. See the {{C[jump="ictags.html#perl"]perl}} and {{C[jump="ictags.html#mvasp"]mvasp}} tags for usage details.
The C<[{{CMD[jump="ictags.html#perl"]perl}}]> tag enforces {{CMD[jump="http://www.perl.com/pub/doc/manual/html/lib/Safe.html"]Safe.pm}} checking, so many standard Perl operators are not available. This prevents user access to all files and programs on the system without the Interchange daemon's permissions. See {{C[jump="icconfig.html#GlobalSub *global*"]GlobalSub}} and {{C[jump="ictags.html#User-defined Tags"]User-defined Tags}} for ways to make external files and programs available to Interchange.
.Named parameters:
.See the {{C[jump="ictags.html#perl"]perl}} tag for a description of the tag parameters and attributes. These N<include:>
!block example
[perl tables="tables-to-open"*
subs=1*
global=1*
no_return=1*
failure="Return value in case of compile or runtime error"*
file="include_file"*]
!endblock
.Required parameters: none
Any Interchange tag (except ones using SQL) can be accessed using the C<$Tag> object. If using SQL queries inside a Perl element, C<AllowGlobal> permissions are required and and the C<global=1> parameter must be set. Installing the module C<Safe::Hole> along with sharing the database table with <tables=tablename> will enable SQL use.
.For example:
!block example; listitem=2
# If the item might contain a single quote
[perl]
$comments = $Values->{comments};
[/perl]
!endblock
Note[label='Important Note: '] Global subroutines are not subject to the stringent security check from the C<Safe> module. This means that the subroutine will be able to modify any variable in Interchange and will be able to write to read and write any file that the Interchange daemon has permission to write. Because of this, the subroutines should be used with caution. They are defined in the main C<interchange.cfg> file and can't be reached by from individual users in a multi-catalog system.
Global subroutines are defined in C<interchange.cfg> with the C<GlobalSub> directive or in user catalogs which have been enabled through C<AllowGlobal>. Catalog subroutines are defined in C<catalog.cfg>, with the C<Sub> directive and are subject to the stringent Safe.pm security restrictions that are controlled by the global directive C<SafeUntrap>.
The code can be as complex as you want them to be, but cannot be used by operators that modify the file system or use unsafe operations like "system," "exec," or backticks. These constraints are enforced with the default permissions of the standard Perl module {{B:Safe}}. Operations may be untrapped on a system-wide basis with the C<SafeUntrap> directive.
The result of this tag will be the result of the last expression evaluated, just as in a subroutine. If there is a syntax error or other problem with the code, there will be no output.
Here is a simple one which does the equivalent of the classic hello.pl program:
!block example
[perl] my $tmp = "Hello, world!"; $tmp; [/perl]
!endblock
There is no need to set the variable. It is there only to show the capability.
To echo the user's browser, but within some HTML tags:
!block example
[perl]
my $html = '<H5>';
$html .= $Session->{browser};
$html .= '</H5>';
$html;
[/perl]
!endblock
To show the user their name and the current time:
!block example
[perl]
my $string = "Hi, " . $Values->{name} ". The time is now ";
$string .= $Tag->time();
$string;
[/perl]
!endblock
H2: ASP-Like Perl
Interchange supports an ASP-like syntax using the E<lbracket>{{C[jump="ictags.html#mvasp"]mvasp}}] tag.
!block example
[mvasp]
<HTML><BODY>
This is HTML.<BR>
<% HTML "This is code<BR>"; %>
More HTML.<BR>
<% $Document->write("Code again.<BR>") %>
[/mvasp]
!endblock
If no closing C<[/mvasp]> tag is present, the remainder of the page will also be seen as ASP.
ASP is simple. Anything between <% and %> is code, and the string %> can not occur anywhere inside. Anything not between those anchors is plain HTML that is placed unchanged on the page. Interchange variables, C<[L][/L]> and C<[LC][/LC]> areas will still be inserted, but any Interchange tags will not.
There is a shorthand <% = $foo %>, which is equivalent to <% $Document->write($foo); %> or <% HTML $foo; %>
!block example
[mvasp]
<HTML><BODY>
This is HTML.<BR>
[value name] will show up as &#91;value name].<BR>
&#95_VARIABLE__ value is equal to: __VARIABLE__
<% = "This is code<BR>" %>
!endblock
The C<__VARIABLE__> will be replaced by the value of C<Variable VARIABLE>, but [value name] will be shown unchanged.
!block note; label="Important Note: "
If using the C<SQL::Statement> module, the catalog must be set to C<AllowGlobal> in C<interchange.cfg>. It will not work in "Safe" mode due to the limitations of object creation in Safe. Also, the C<Safe::Hole> module must be installed to have SQL databases work in Safe mode.
!endblock
H2: Error Reporting
If your Perl code fails with a compile or runtime error, Interchange writes the error message from the Perl interpreter into the catalog's error log. This is usually 'I<catalog_root>/error.log'. Error messages do not appear on your web page as the return value of the Perl tag or routine.
You will not have direct access to the 'C<strict>' and 'C<warnings>' pragmas where Interchange runs your perl code under C<Safe> (for example, within a [perl] or [mvasp] tag).
H1: Interchange Perl Objects
.Interchange gives you access to the power of Perl with the [perl], [calc] and
.[mvasp] tags. They all support the same set of Perl objects and variables.
H2: A note about Safe
You can access all objects associated with the catalog and the user settings with opcode restrictions based on the standard Perl module {{C[jump="http://www.perl.com/pub/doc/manual/html/lib/Safe.html"]Safe.pm}}. There are some unique things to know about programming with Interchange.
Under C<Safe>, certain things cannot be used. For instance, the following can not be used when running Safe:
!block example
$variable = `cat file/contents`;
!endblock
The backtick operator violates a number of the default Safe opcode restrictions. Also, direct file opens can not be used. For example:
!block example
open(SOMETHING, "something.txt")
or die;
!endblock
This will also cause a trap, and the code will fail to compile. However, equivalent Interchange routines can be used:
!block example
# This will work if your administrator doesn't have NoAbsolute set
$users = $Tag->file('/home/you/list');
# This will always work, file names are based in the catalog directory
$users = $Tag->file('userlist');
!endblock
H2: Standard objects and variables
The following is a list of Interchange Perl standard objects are:
LI1: $CGI
.This is a hash reference to C<%CGI::values>, the value of user variables as submitted in the current page/form. To get the value of a variable submitted as
!block example; listitem=2
<INPUT TYPE=hidden NAME=foo VALUE=bar>
!endblock
.use
!block example; listitem=2
[perl]
return "Value of foo is $CGI->{foo}";
[/perl]
!endblock
Actually, you should not do that -- if someone sends you a value you should
not output it willy-nilly for security reasons. Filter it first with the [filter]
tag as accessed by the $Tag object:
!block example; listitem=2
[perl]
my $val = $Tag->filter('encode_entities', $CGI->{foo});
return "Value of foo is $val";
[/perl]
!endblock
.Remember, multiple settings of the same variable are separated by a NULL character. To get the array value, use $CGI_array.
LI1: $CGI_array
.This is a hash reference to C<%CGI::values_array>, arrays containing the value or values of user variables as submitted in the current page/form. To get the value of a variable submitted as
!block example; listitem=2
<INPUT TYPE=hidden NAME=foo VALUE='bar'>
<INPUT TYPE=hidden NAME=foo VALUE='baz'>
!endblock
.use
!block example; listitem=2
<% = "The values of foo are", join (' and ', @{$CGI_array->{'foo'}}) %>
!endblock
.Remember, multiple settings of the same variable are separated by a NULL character. To get the array value, use $CGI_array.
LI1: $Carts
.A reference to the shopping cart hash $Vend::Session->{carts}. The normal default cart is "main". A typical alias is $Items.
.Shopping carts are an array of hash references. Here is an example of a session cart array containing a C<main> and a C<layaway> cart.
!block example; listitem=2
{
'main' => [
{
'code' => '00-0011',
'mv_ib' => 'products',
'quantity' => 1,
'size' => undef,
'color' => undef
},
{
'code' => '99-102',
'mv_ib' => 'products',
'quantity' => 2,
'size' => 'L',
'color' => 'BLUE'
}
],
'layaway' => [
{
'code' => '00-341',
'mv_ib' => 'products',
'quantity' => 1,
'size' => undef,
'color' => undef
}
]
}
!endblock
.In this cart array, $Carts->{main}[1]{code} is equal to C<99-102>. Normally, it would be equivalent to $Items->[1]{code}.
LI1: $Config
.A reference to the $Vend::Cfg array. This is normally used with a large amount of the Interchange source code, but for simple things use something like:
!block example;
# Allow searching the User database this page only
$Config->{NoSearch} =~ s/\buserdb\b//;
!endblock
.Changes are not persistent -- they are reset upon the next page access.
LI1: %Db
.A hash of databases shared with the C<tables="foo"> parameter to the C<[perl]> resp. C<[mvasp]> tag calls. Once the database is shared, it is open and can be accessed by any of its methods. This will not work with SQL unless the C<Safe::Hole> module is installed or AllowGlobal is set for the catalog.
{{B:NOTE}}: This object is not present and the below will not work with [calc].
.To get a reference to a particular table, specify its hash element:
!block example; listitem=2
my $db = $Db{products};
!endblock
.The available methods are:
!block example; listitem=2
# Key for a normal table with one primary key
$key = 'foo';
# Array reference key for a COMPOSITE_KEY table
$composite_ary_key = ['foo','bar','buz'];
# Alternate hash reference key for a COMPOSITE_KEY table
$composite_hash_key = { key1 => 'foo', key2 => 'bar', key3 => 'buz'};
# Alternate null-separated key for a COMPOSITE_KEY table
$composite_nullsep_key = join "\0", 'foo','bar','buz';
### Any of the composite key types may be substitued
### when COMPOSITE_KEY table
# access an element of the table
$field = $db->field($key, $column);
# set an element of the table
$db->set_field($key, $column_name, $value);
# atomic increment of an element of the table
$db->inc_field($key, $column_name, 1);
# Return a complete hash of the database row (minus the key)
$hashref = $db->row_hash($key);
# Return some fields from a row
my @fields = qw/sku price description/;
@values = $db->get_slice($key, \@fields);
# Set some fields in a row (slice)
my $key = 'os28004';
my @fields = qw/price description/;
my @values = (5.95, "Ergo Roller");
$array_ref = $db->set_slice($key, \@fields, \@values);
# Alternate way to set slice
my $key = 'os28004';
my %fields = ( price => 5.95, description => "Ergo Roller");
$array_ref = $db->set_slice($key, \%fields);
# Perform a SQL query, returning an array of arrays
# (the equivalent of DBI $sth->fetchall_arrayref)
$ary = $db->query($sql);
# Same as above, except receive
# hash reference of pointers to field positions and
# array reference containing list of fields
my $sql = 'select * from products';
($ary, $index_hash, $name_ary) = $db->query($sql);
$fields_returned = join ",", @$name_ary;
$pointer_to_price = $index_hash->{price};
# Perform a SQL query, returning an array of hashes
$ary = $db->query({ sql => $sql, hashref => 1 });
# see if element of the table is numeric
$is_numeric = $db->numeric($column_name);
# Quote for SQL query purposes
$quoted = $db->quote($value, $column_name);
# Check configuration of the database
$delimiter = $db->config('DELIMITER');
# Find the names of the columns (not including the key)
@columns = $db->columns();
# Insert the key column name
unshift @columns, $db->config('KEY');
# See if a column is in the table
$is_a_column = defined $db->test_column($column_name);
# See if a row is in the table
$is_present = $db->record_exists($key);
# Create a subroutine to return a single column from the table
$sub = $db->field_accessor($column);
for (@keys) {
push @values, $sub->($key);
}
# Create a subroutine to set a single column in the database
$sub = $db->field_settor($column);
for (@keys) {
$sub->($key, $value);
}
# Create a subroutine to set a slice of the database
$sub = $db->row_settor(@columns);
for (@keys) {
$sub->($key, @values);
}
# Return a complete array of the database (minus the key)
@values = $db->row($key);
# Delete a record/row from the table
$db->delete_record($key);
!endblock
LI1: %Sql
.A hash of SQL databases that you shared with the C<[perl tables="foo"]> parameter to the tag call. It returns the DBI database handle, so operations like the following can be performed:
{{B:NOTE}}: This object is not present and the below will not work with [calc].
!block example; listitem=2
[perl products]
my $dbh = $Sql{products}
or return "Database not shared.";
my $sth = $dbh->prepare('select * from products')
or return "Couldn't open database.";
$sth->execute();
my @record;
while(@record = $sth->fetchrow()) {
foo();
}
$sth = $dbh->prepare('select * from othertable')
or return "Couldn't open database.";
$sth->execute();
while(@record = $sth->fetchrow()) {
bar();
}
[/perl]
!endblock
LI1: $DbSearch
.A search object that will search a database without using the text file. It is the same as Interchange's C<db> searchtype. Options are specified in a hash and passed to the object. All multiple-field options should be passed as array references. Before using the $DbSearch object, it must be told which table to search. For example, to use the table C<foo>, it must have been shared with C<[mvasp foo]>.
.There are three search methods: C<array>, C<hash> and C<list>.
!block example; listitem=2
array Returns a reference to an array of arrays (best)
hash Returns a reference to an array of hashes (slower)
list Returns a reference to an array of tab-delimited lines
!endblock
.\Example:
!block example; listitem=2
$DbSearch->{table} = $Db{foo};
$search = {
mv_searchspec => 'Mona Lisa',
mv_search_field => [ 'title', 'artist', 'price' ],
mv_return_fields => [ 'title' ]
};
my $ary = $DbSearch->array($search);
if(! scalar @$ary) {
return HTML "No match.\n";
}
for(@$ary) {
!endblock
LI1: $Document
This is an object which will allow you to write and manipulate the output
of your embedded Perl. For instance, you can emulate a non-parsed-header
program with:
!block example; listitem=2
[perl]
$Document->hot(1);
for(1 .. 20) {
$Document->write("Counting to $_...<br>");
$Document->write( " " x 4096);
$Tag->sleep(1);
}
$Document->write("Finished counting!");
return;
[/perl]
!endblock
Note the write of 4096 spaces. Because Interchange's link program is
parsed by default and your web server (and the link program) have
buffers, you need to fill up the buffer to cause a write. You can do it
without the extra padding if you set the link up as a non-parsed-header
program -- see your web server documentation on how to do that.
There are several methods associated with $Document:
!block example; listitem=2
HTML $foo; # Append $foo to the write buffer array
$Document->write($foo); # object call to append $foo to the write
# buffer array
$Document->insert($foo); # Insert $foo to front of write buffer array
$Document->header($foo, $opt); # Append $foo to page header
$Document->send(); # Send write buffer array to output, done
# automatically upon end of ASP, clears buffer
# and invalidates $Document->header()
$Document->hot(1); # Cause writes to send immediately
$Document->hot(0); # Stop immediate send
@ary = $Document->review(); # Place contents of write buffer in @ary
$Document->replace(@ary) # Replace contents of write buffer with @ary
$ary_ref = $Document->ref(); # Return ref to output buffer
!endblock
LI1: $Document->write($foo)
.Write $foo to the page in a buffered fashion. The buffer is an array containing the results of all previous C<$Document->>C<write()> operations. If $Document->hot(1) has been set, the output immediately goes to the user.
LI1: $Document->insert($foo)
.Insert $foo to the page buffer. The following example will output "123"
!block example; listitem=2
$Document->write("23");
$Document->insert("1");
$Document->send();
!endblock
.while this example will output "231"
!block example; listitem=2
$Document->write("23");
$Document->write("1");
$Document->send();
!endblock
.will output "231".
LI1: $Document->header($foo, $opt)
.Add the header line $foo to the HTTP header. This is used to change the page content type, cache options or other attributes. The code below changes the content type (MIME type) to text/plain:
!block example; listitem=2
$Document->header("Content-type: text/plain");
!endblock
.There is an optional hash that can be sent with the only valid value being "replace." The code below scrubs all previous header lines:
!block example; listitem=2
$Document->header("Content-type: text/plain", { replace => 1 } );
!endblock
.Once output has been sent with $Document->send(), this can no longer be done.
LI1: $Document->hot($foo)
.If the value of $foo is true (in a Perl sense), then all $Document->write() operations will be immediately sent until a $Document->hot(0) is executed.
LI1: $Document->send()
.Causes the document write buffer to be sent to the browser and empties the buffer. Any further $Document->header() calls will be ignored. Can be used to implement non-parsed-header operation.
LI1: $Document->review()
.Returns the value of the write buffer.
!block example; listitem=2
@ary = $Document->review();
!endblock
LI1: $Document->replace(@new)
.Completely replaces the write buffer with the arguments.
LI1: $Document->ref()
.Returns a reference to the write buffer.
!block example; listitem=2
# Remove the first item in the write buffer
my $ary_ref = $Document->ref();
shift @$ary_ref;
!endblock
LI1: HTML
.Writes a string (or list of strings) to the write buffer array. The call
!block example; listitem=2
HTML $foo, $bar;
!endblock
.is exactly equivalent to
!block example; listitem=2
$Document->write($foo, $bar);
!endblock
.Honors the $Document->hot() setting.
LI1: $Items
.A reference to the current shopping cart. Unless an Interchange C<[cart ...]> tag is used, it is normally the same as $Carts->{main}.
LI1: $Scratch
.A reference to the scratch values ala C<[scratch foo]>.
!block example; listitem=2
<% $Scratch->{foo} = 'bar'; %>
!endblock
.is equivalent to:
!block example; listitem=2
[set foo]bar[/set]
!endblock
LI1: $Session
.A reference to the session values ala C<[data session username]>.
!block example; listitem=2
<%
my $out = $Session->{browser};
$Document->write($out);
%>
!endblock
.is equivalent to:
!block example; listitem=2
[data session browser]
!endblock
.Values can also be set. If the value of C<[data session source]> needed to be changed, for example, set:
!block example; listitem=2
<%
$Session->{source} = 'New_partner';
%>
!endblock
LI1: $Tag
.Using the $Tag object, any Interchange tag including user-defined tags can be accessed.
Note[label='IMPORTANT NOTE: '] If the tag will access a database that has not been previously opened, the table name must be passed in the ITL call. For example:
.Named parameters:
!block example; listitem=2
[perl tables="products pricing"]
!endblock
.or
.Positional parameters:
!block example; listitem=2
[perl products pricing]
!endblock
.Any tag can be called.
!block example; listitem=2
[perl]
my $user = $Session->{username};
return $Tag->data('userdb', 'name', $user );
[/perl]
!endblock
.is the same as:
!block example; listitem=2
[data table=userdb column=name key="[data session username]"]
!endblock
.If the tag has a dash (C<->) in it, use an underscore instead:
!block example; listitem=2
# WRONG!!!
$Tag->shipping-desc('upsg');
# Right
$Tag->shipping_desc('upsg');
!endblock
.There are two ways of specifying parameters. Either use the positional parameters as documented (for an authoritative look at the parameters, see the %Routine value in Vend::Parse) or specify it all with an option hash parameter names as in any named parameters as specified in an Interchange tag. The calls
!block example; listitem=2
$Tag->data('products', 'title', '00-0011');
!endblock
.and
!block example; listitem=2
my $opt = {
table => 'products',
column => 'title',
key => '00-0011',
};
$Tag->data( $opt );
!endblock
.are equivalent for the C<data> tag.
.If using the option hash method, and the tag has container text, either specify it in the hash parameter C<body> or add it as the next argument. The two calls:
!block example; listitem=2
$Tag->item_list( {
'body' => "[item-code] [item-field title]",
});
!endblock
.and
!block example; listitem=2
$Tag->item_list( { }, "[item-code] [item-field title]")
!endblock
.are equivalent.
.Parameter names are ALWAYS lower case.
LI1: $Values
.A reference to the user form values ala C<[value foo]>.
!block example; listitem=2
<% $Document->write($Values->{foo}); %>
!endblock
.is equivalent to:
!block example; listitem=2
[value foo]
!endblock
LI1: &Log
.Send a message to the error log (same as ::logError in GlobalSub or global UserTag).
!block example; listitem=2
<%
Log("error log entry");
%>
!endblock
.It prepends the normal timestamp with user and page information. To
suppress that information, begin the message with a backslash (C<\>).
!block example; listitem=2
<%
Log("\\error log entry without timestamp");
Log('\another error log entry without timestamp');
Log("error log entry with timestamp");
%>
!endblock
H1: Debugging
No debug output is provided by default. The source files contain commented-out '::logDebug(SOMETHING)' statements which can be edited to activate them. Set the value of C<DebugFile> to a file that will be written to:
!block example
DebugFile /tmp/icdebug
!endblock
H2: Export
.Named Parameters: [export table="dbtable"]
.Positional Parameters: [export db_table]
.The attribute hash reference is passed to the subroutine after the parameters as the last argument. This may mean that there are parameters not shown here. Must pass named parameter interpolate=1 to cause interpolation.
.Invalidates cache: YES
.Called Routine:
.ASP/perl tag calls:
!block example; listitem=2
$Tag->export(
{
table => VALUE,
}
)
!endblock
.OR
!block example; listitem=2
$Tag->export($table, $ATTRHASH);
!endblock
.Attribute aliases:
!block example; listitem=2
base ==> table
database ==> table
!endblock
H2: Time
.Named Parameters: [time locale="loc"]
.Positional Parameters: [time loc]
.The attribute hash reference is passed after the parameters but before the container text argument. This may mean that there are parameters not shown here. Must pass named parameter interpolate=1 to cause interpolation.
.This is a container tag, i.e., [time] FOO [/time].
.Nesting: NO.
.Invalidates cache: NO.
.Called Routine:
.ASP/perl tag calls:
!block example; listitem=2
$Tag->time(
{
locale => VALUE,
},
BODY
)
!endblock
.OR
!block example; listitem=2
$Tag->time($locale, $ATTRHASH, $BODY);
!endblock
H2: Import
.Named Parameters: [import table=table_name type=(TAB|PIPE|CSV|%%|LINE) continue=(NOTES|UNIX|DITTO) separator=c]
.Positional Parameters: [import table_name TAB]
.The attribute hash reference is passed after the parameters but before the container text argument. This may mean that there are parameters not shown here. Interpolates container text by default>.
.This is a container tag, i.e., [import] FOO [/import].
.Nesting: NO
.Invalidates cache: YES.
.Called Routine:
.ASP/perl tag calls:
!block example; listitem=2
$Tag->import(
{
table => VALUE,
type => VALUE,
},
BODY
)
!endblock
.OR
!block example; listitem=2
$Tag->import($table, $type, $ATTRHASH, $BODY);
!endblock
.Attribute aliases:
!block example; listitem=2
base ==> table
database ==> table
!endblock
.Description:
.Import one or more records into a database. The type is any of the valid Interchange delimiter types, with the default being defined by the setting of the database DELIMITER. The table must already be a defined Interchange database table; it cannot be created on-the-fly. (Use SQL for on-the-fly tables.)
.The type of LINE and continue setting of NOTES is particularly useful, for it allows the naming of fields so that the order in which they appear in the database will not have to be remembered. The following two imports are identical in effect:
!block example; listitem=2
[import table=orders type=LINE continue=NOTES]
code: [value mv_order_number]
shipping_mode: [shipping-description]
status: pending
[/import]
[import table=orders type=LINE continue=NOTES]
shipping_mode: [shipping-description]
status: pending
code: [value mv_order_number]
[/import]
!endblock
.The code or key must always be present, and is always named code. If NOTES mode is not used, import the fields in the same order as they appear in the ASCII source file. The [import ....] TEXT [/import] region may contain multiple records. If using NOTES mode, use a separator, which by default is a form-feed character (^L).
H2: Log
.Named Parameters: [log file=file_name]
.Positional Parameters: [log file_name]
.The attribute hash reference is passed after the parameters but before the container text argument. This may mean that there are parameters not shown here. Must pass named parameter interpolate=1 to cause interpolation. This is a container tag, i.e., [log] FOO [/log].
.Nesting: NO.
.Invalidates cache: NO.
.Called Routine:
.ASP/perl tag calls:
!block example; listitem=2
$Tag->log(
{
file => VALUE,
},
BODY
)
!endblock
.OR
!block example; listitem=2
$Tag->log($file, $ATTRHASH, $BODY);
!endblock
.Attribute aliases:
!block example; listitem=2
arg ==> file
!endblock
H2: Header
H2: price, description, accessories
LI1: [price code quantity* database* noformat*]
.named attributes: C<[price code="code" quantity="N" base="database" noformat=1* optionX="value"]>
.Expands into the price of the product identified by code as found in the products database. If there is more than one products file defined, they will be searched in order unless constrained by the optional argument base. The optional argument quantity selects an entry from the quantity price list. To receive a raw number, with no currency formatting, use the option C<noformat=1>.
.If an named attribute corresponding to a product option is passed, and that option would cause a change in the price, the appropriate price will be displayed.
.Demo example: The T-Shirt (product code 99-102), with a base price of $10.00, can vary in price depending on size and color. C<S>, the small size, is 50 cents less; C<XL>, the extra large size, is $1.00 more and the color C<RED> is 0.75 extra. There are also quantity pricing breaks (see the demo C<pricing> database. So the following will be true:
!block example; listitem=2
[price code=99-102
size=L] is $10.00
[price code=99-102
size=XL] is $11.00
[price code=99-102
color=RED
size=XL] is $11.75
[price code=99-102
size=XL
quantity=10] is $10.00
[price code=99-102
size=S] is $9.50
!endblock
.An illustration of this is on the simple C<flypage> template when passed that item code.
LI1: [description code table*]
.named attributes: C<[description code="code" base="database"]>
.Expands into the description of the product identified by code as found in the products database. If there is more than one products file defined, they will be searched in order unless constrained by the optional argument table.
LI1: [accessories code attribute*, type*, field*, database*, name*, outboard*]
.named attributes: C<[accessories code="code"> C<arg="attribute*, type*, field*, database*, name*, outboard*"]>
.Initiates special processing of item attributes based on entries in the product database. See Item Attributes for a complete description of the arguments.
.When called with an attribute, the database is consulted and looks for a comma-separated list of attribute options. They take the form:
!block example; listitem=2
name=Label Text, name=Label Text*
!endblock
.The label text is optional. If none is given, the B<name> will be used.
.If an asterisk is the last character of the label text, the item is the default selection. If no default is specified, the first will be the default. An example:
!block example; listitem=2
[accessories TK112 color]
!endblock
.This will search the product database for a field named "color." If an entry "beige=Almond, gold=Harvest Gold, White*, green=Avocado" is found, a select box like this will be built:
!block example; listitem=2
<SELECT NAME="mv_order_color">
<OPTION VALUE="beige">Almond
<OPTION VALUE="gold">Harvest Gold
<OPTION SELECTED>White
<OPTION VALUE="green">Avocado
</SELECT>
!endblock
.In combination with the C<mv_order_item> and C<mv_order_quantity> variables, this can be used to allow entry of an attribute at time of order.
H2: FILE and INCLUDE
These elements read a file from the disk and insert the contents in the location of the tag. C<[include ...]> will allow insertion of Interchange variables and ITL tags.
LI1: [file ...]
.named: [file name="name" type="dos|mac|unix"*]
.positional: [file name]
.Inserts the contents of the named file. The file should normally be relative to the catalog directory. File names beginning with / or .. are only allowed if the Interchange server administrator has disabled NoAbsolute. The optional C<type> parameter will do an appropriate ASCII translation on the file before it is sent.
LI1: [include file]
.named attributes: C<[include file="name"]>
.Same as C<[file name]> except interpolates for all Interchange tags and variables.
H2: Banner/Ad rotation
Interchange has a built-in banner rotation system designed to show ads or other messages according to category and an optional weighting.
The C<[banner ...]> ITL tag is used to implement it.
The weighting system pre-builds banners in the directory 'Banners,' under the temporary directory. It will build one copy of the banner for every one weight. If one banner is weighted C<7>, one C<2> and one C<1>, then a total of 10 pre-built banners will be made. The first will be displayed 70 percent of the time, the second 20 percent and the third 10 percent, in random fashion. If all banners need to be equal, give each a weight of 1.
Each category may have separate weighting. If the above is placed in category C<tech>, then it will behave as above when placed in C<[banner category=tech]> in the page. A separate category, say C<art>, would have its own rotation and weighting.
The C<[banner ...]> tag is based on a database table, named C<banners> by default. It expects a total of five (5) fields in the table:
LI1: code
.This is the key for the item. If the banners are not weighted, this should be a category specific code.
LI1: category
.To choose to categorize weighted ads, this contains the category to select. If empty, it will be placed in the default (or blank) category.
LI1: weight
.Must be an integer number 1 or greater to include this ad in the weighting. If 0 or blank, the ad will be ignored when weighted ads are built.
LI1: rotate
.If the weighted banners are not used, this must contain some value. If the field is empty, the banner will not be displayed. If the value is specifically C<0> (zero), then the entire contents of the C<banner> field will be displayed when this category is used. If it is non-zero, then the contents of the C<banner> field will be split into segments (by the separator C<{or}>). For each segment, the banners will rotate in sequence for that user only. Obviously, the first banner in the sequence is more likely to be displayed than the last.
.Summary of values of rotate field:
!block example; listitem=2
non-zero, non-blank: Rotating ads
blank: Ad not displayed
0: Ad is entire contents of banner field
!endblock
LI1: banner
.This contains the banner text. If more than one banner is in the field, they should be separated by the text C<{or}> (which will not be displayed).
Interchange expects the C<banner> field to contains the banner text. It can contain more than one banner, separated by the string '{or}.' To activate the ad, place any string in the field C<rotate>.
The special key "default" is the banner that is displayed if no banners are found. (Doesn't apply to weighted banners.)
Weighted banners are built the first time they are accessed after catalog reconfiguration. They will not be rebuilt until the catalog is reconfigured or the file tmp/Banners/total_weight and tmp/Banners/<category>/total_weight is removed.
If the option C<once> is passed (i.e., [banner once=1 weighted=1], then the banners will not be rebuilt until the C<total_weight> file is removed.
The database specification should make the C<weight> field numeric so that the proper query can be made. Here is the example from Interchange's demo:
!block example
Database banner banner.txt TAB
Database banner NUMERIC weight
!endblock
\Examples:
LI1: weighted, categorized
.To select categorized and weighted banners:
.The C<banner> table would look like this:
!block example; listitem=2
code category weight rotate banner
t1 tech 1 Click here for a 10% banner
t2 tech 2 Click here for a 20% banner
t3 tech 7 Click here for a 70% banner
a1 art 1 Click here for a 10% banner
a2 art 2 Click here for a 20% banner
a3 art 7 Click here for a 70% banner
!endblock
.Tag would be:
!block example; listitem=2
[banner weighted=1 category="tech"]
!endblock
.This will find *all* banners with a weight >= 1 where the C<category> field is equal to C<tech>. The files will be made into the director C<tmp/Banners/tech>.
LI1: weighted
.To select weighted banners:
!block example; listitem=2
[banner weighted=1]
!endblock
.This will find *all* banners with a weight >= 1. (Remember, integers only.) The files will be made into the director C<tmp/Banners>.
!block example; listitem=2
code category weight rotate banner
t1 tech 1 Tech banner 1
t2 tech 2 Tech banner 2
t3 tech 7 Tech banner 3
a1 art 1 Art banner 1
a2 art 2 Art banner 2
a3 art 7 Art banner 3
!endblock
.Each of the above with a weight of 7 will actually be displayed 35 percent of the time.
LI1: categorized, not rotating
!block example; listitem=2
[banner category="tech"]
!endblock
.This is equivalent to:
!block example; listitem=2
[data table=banner col=banner key=tech
!endblock
.The differences are that it is not selected if "rotate" field is blank; if not selected, the default banner is displayed.
.The C<banner> table would look like this:
!block example; listitem=2
code category weight rotate banner
tech 0 0 Tech banner
!endblock
.Interchange tags can be inserted in the category parameter, if desired:
!block example; listitem=2
[banner category="[value interest]"]
!endblock
LI1: categorized and rotating
!block example; listitem=2
[banner category="tech"]
!endblock
.The difference between this and above is the database.
.The C<banner> table would look like this:
!block example; listitem=2
code category weight rotate banner
tech 0 1 Tech banner 1{or}Tech banner 2
art 0 1 Art banner 1{or}Art banner 2
!endblock
.This would rotate between banner 1 and 2 for the category tech for each user. Banner 1 is always displayed first. The C<art> banner would never be displayed unless the tag C<[banner category=art]> was used, of course.
.Interchange tags can be inserted in the category parameter, if desired:
!block example; listitem=2
[banner category="[value interest]"]
!endblock
LI1: multi-level categorized
!block example; listitem=2
[banner category="tech:hw"] or [banner category="tech:sw"]
!endblock
.If have a colon-separated category, Interchange will select the most specific ad available. If the C<banner> table looks like this:
!block example; listitem=2
code category weight rotate banner
tech 0 1 Tech banner 1{or}Tech banner 2
tech:hw 0 1 Hardware banner 1{or}HW banner 2
tech:sw 0 1 Software banner 1{or}SW banner 2
!endblock
.This works the same as single-level categories, except that the category tech:hw will select that banner. The category tech:sw will select its own. But, the category tech:html would just get the "tech" banner. Otherwise, it works just as in other categorized ads. Rotation will work if set non-zero/non-blank and it will be inactive if the rotate field is blank. Each category rotates on its own.
LI1: Advanced
.All parameters are optional since they are marked with an asterisk C<(*)>.
.Tag syntax:
!block example; listitem=2
[banner
weighted=1*
category=category*
once=1*
separator=sep*
delimiter=delim*
table=banner_table*
a_field=banner_field*
w_field=weight_field*
r_field=rotate_field*
]
!endblock
.Defaults are blank except:
!block example; listitem=2
table banner selects table used
a_field banner selects field for banner text
delimiter {or} delimiter for rotating ads
r_field rotate rotate field
separator : separator for multi-level categories
w_field weight rotate field
!endblock
H2: Tags for Summarizing Shopping Basket/Cart
The following elements are used to access common items which need to be displayed on baskets and checkout pages.
B<* marks an optional parameter>
LI1: [item-list cart*]
.named attributes: C<[item-list name="cart"]>
.Places an iterative list of the items in the specified shopping cart, the main cart by default. See Item Lists for a description.
LI1: [/item-list]
.Terminates the C<[item-list]> tag.
LI1: [nitems cart*]
.Expands into the total number of items ordered so far. Takes an optional cart name as a parameter.
LI1: [subtotal]
.Expands into the subtotal cost, exclusive of sales tax, of all the items ordered so far.
LI1: [salestax cart*]
.Expands into the sales tax on the subtotal of all the items ordered so far. If there is no key field to derive the proper percentage, such as state or zip code, it is set to 0. See SALES TAX for more information.
LI1: [shipping-description mode*]
.named attributes: C<[shipping-description name="mode"]>
.The text description of mode. The default is the shipping mode currently selected.
LI1: [shipping mode*]
.named attributes: C<[shipping name="mode"]>
.The shipping cost of the items in the basket via C<mode>. The default mode is the shipping mode currently selected in the C<mv_shipmode> variable. See SHIPPING.
LI1: [total-cost cart*]
.Expands into the total cost of all the items in the current shopping cart, including sales tax, if any.
LI1: [currency convert*]
.named attributes: C<[currency convert=1*]>
.When passed a value of a single number, formats it according to the currency specification. For instance:
!block example; listitem=2
[currency]4[/currency]
!endblock
.will display:
!block example; listitem=2
4.00
!endblock
.Uses the Locale and PriceCommas settings as appropriate, and can contain a C<[calc]> region. If the optional "convert" parameter is set, it will convert according to PriceDivide> for the current locale. If Locale is set to C<fr_FR>, and F<PriceDivide> for C<fr_FR> is 0.167, using the following sequence:
!block example; listitem=2
[currency convert=1] [calc] 500.00 + 1000.00 [/calc] [/currency]
!endblock
.will cause the number 8.982,04 to be displayed.
LI1: [/currency]
.Terminates the currency region.
LI1: [cart name]
.named attributes: C<[cart name="name"]>
.Sets the name of the current shopping cart for display of C<[shipping]>, C<[price]>, C<[total]>, C<[subtotal]> and C<[nitems]> tags. If a different price is used for the cart, all of the above except C<[shipping]> will reflect the normal price field. Those operations must be emulated with embedded Perl or the C<[item-list]>, C<[calc]> and C<[currency]> tags, or use the PriceAdjustment feature to set it.
LI1: [row nn]
.named attributes: C<[row width="nn"]>
.Formats text in tables. Intended for use in emailed reports or <PRE></PRE> HTML areas. The parameter nn gives the number of columns to use. Inside the row tag, C<[col param=value ...]> tags may be used.
LI1: [/row]
.Terminates a C<[row nn]> element.
LI1: [col width=nn wrap=yes|no gutter=n align=left|right|input spacing=n]
.Sets up a column for use in a C<[row]>. This parameter can only be contained inside a C<[row nn] [/row]> tag pair. Any number of columns (that fit within the size of the row) can be defined.
.The parameters are:
!block example; listitem=2
width=nn The column width, including the gutter. Must be
supplied, there is no default. A shorthand method
is to just supply the number as the first parameter,
as in [col 20].
gutter=n The number of spaces used to separate the column (on
the right-hand side) from the next. Default is 2.
spacing=n The line spacing used for wrapped text. Default is 1,
or single-spaced.
wrap=(yes|no) Determines whether text that is greater in length than
the column width will be wrapped to the next line. Default
is yes.
align=(L|R|I) Determines whether text is aligned to the left (the default),
the right or in a way that might display an HTML text
input field correctly.
!endblock
LI1: [/col]
.Terminates the column field.
H2: Item Lists
Within any page, the C<[item-list cart*]> element shows a list of all the items ordered by the customer so far. It works by repeating the source between C<[item-list]> and C<[/item-list]> once for each item ordered.
Note: The special tags that reference item within the list are not normal Interchange tags, do not take named attributes and cannot be contained in an HTML tag (other than to substitute for one of its values or provide a conditional container). They are interpreted only inside their corresponding list container. Normal Interchange tags can be interspersed, though they will be interpreted I<after> all of the list-specific tags.
Between the item_list markers the following elements will return information for the current item:
LI1: [if-item-data table column]
.If the database field C<column> in table I<table> is non-blank, the following text up to the C<[/if-item-data]> tag is substituted. This can be used to substitute IMG or other tags only if the corresponding source item is present. Also accepts a C<[else]else text[/else]> pair for the opposite condition.
Note: This tag does not nest with other C<[if-item-data ...]> tags.
LI1: [if-item-data table column]
.Reverses sense for C<[if-item-data]>.
LI1: [/if-item-data]
.Terminates an C<[if-item-data table column]> element.
LI1: [if-item-field fieldname]
.If the products database field fieldname is non-blank, the following text up to the C<[/if-item-field]> tag is substituted. If there are more than one products database table (see ProductFiles), it will check them in order until a matching key is found. This can be used to substitute IMG or other tags only if the corresponding source item is present. Also accepts a C<[else]else text[/else]> pair for the opposite condition.
Note: This tag does not nest with other C<[if-item-field ...]> tags.
LI1: [if-item-field fieldname]
.Reverses sense for C<[if-item-field]>.
LI1: [/if-item-field]
.Terminates an C<[if-item-field fieldname]> element.
LI1: [item-accessories attribute*, type*, field*, database*, name*]
.Evaluates to the value of the Accessories database entry for the item. If passed any of the optional arguments, initiates special processing of item attributes based on entries in the product database.
LI1: [item-alternate N] DIVISIBLE [else] NOT DIVISIBLE [/else][/item-alternate]
.Sets up an alternation sequence. If the item-increment is divisible by C<N>, the text will be displayed. If an C<[else]NOT DIVISIBLE TEXT[/else]> is present, the NOT DIVISIBLE TEXT will be displayed.
.For example:
!block example; listitem=2
[item-alternate 2]EVEN[else]ODD[/else][/item-alternate]
[item-alternate 3]BY 3[else]NOT by 3[/else][/item-alternate]
!endblock
LI1: [/item-alternate]
.Terminates the alternation area.
LI1: [item-code]
.Evaluates to the product code for the current item.
LI1: [item-data database fieldname]
.Evaluates to the field name fieldname in the arbitrary database table database for the current item.
LI1: [item-description]
.Evaluates to the product description (from the products file) for the current item.
LI1: [item-field fieldname]
.The [item-field ...] tag is special in that it looks in any of the tables defined as C<ProductFiles>, in that order, for the data, returning the value only if that key is defined. In most catalogs, where C<ProductFiles> is not defined (i.e., the demo), C<[item-field title]> is equivalent to C<[item-data products title]>.
.Evaluates to the field name fieldname in the products database for the current item. If the item is not found in the first of the ProductFiles, all will be searched in sequence.
LI1: [item-increment]
.Evaluates to the number of the item in the match list. Used for numbering search matches or order items in the list.
LI1: [item-last]tags[/item-last]
.Evaluates the output of the Interchange tags encased inside the tags. If it evaluates to a numerical non-zero number (i.e., 1, 23, or -1), the list iteration will terminate. If the evaluated number is negative, the item itself will be skipped. If the evaluated number is positive, the item itself will be shown but will be last on the list.
!block example; listitem=2
[item-last][calc]
return -1 if '[item-field weight]' eq '';
return 1 if '[item-field weight]' < 1;
return 0;
[/calc][/item-last]
!endblock
.If this is contained in the C<[item-list]> (or C<[search-list]> or flypage) and the weight field is empty, a numerical C<-1> will be output from the C<[calc][/calc]> tags; the list will end and the item will B<not> be shown. If the product's weight field is less than 1, a numerical 1 is output. The item will be shown, but will be the last item shown. (If it is an C<[item-list]>, any price for the item will still be added to the subtotal.) NOTE: there is no equivalent HTML style.
LI1: [item-modifier attribute]
.Evaluates to the modifier value of C<attribute> for the current item.
LI1: [item-next]tags[/item_next]
.Evaluates the output of the Interchange tags encased inside. If it evaluates to a numerical non-zero number (i.e., 1, 23, or -1), the item will be skipped with no output. Example:
!block example; listitem=2
[item-next][calc][item-field weight] < 1[/calc][/item-next]
!endblock
.If this is contained in the C<[item-list]> (or C<[search-list]> or flypage) and the product's weight field is less than 1, a numerical C<1> will be output from the [calc][/calc] operation. The item will not be shown. (If it is an C<[item-list]>, any price for the item will still be added to the subtotal.)
LI1: [item-price n* noformat*]
.Evaluates to the price for quantity C<n> (from the products file) of the current item, with currency formatting. If the optional "noformat" is set, currency formatting will not be applied.
LI1: [item-discount-price n* noformat*]
.Evaluates to the discount price for quantity C<n> (from the products file) of the current item, with currency formatting. If the optional "noformat" is set, currency formatting will not be applied. Returns regular price if not discounted.
LI1: [item-discount]
.Returns the difference between the regular price and the discounted price.
LI1: [item-quantity]
.Evaluates to the quantity ordered for the current item.
LI1: [item-subtotal]
.Evaluates to the subtotal (quantity * price) for the current item. Quantity price breaks are taken into account.
LI1: [item-modifier-name attribute]
.Evaluates to the name to give an input box in which the customer can specify the modifier to the ordered item.
LI1: [quantity-name]
.Evaluates to the name to give an input box in which the customer can enter the quantity to order.
H1: Interchange Page Display
Interchange has several methods for displaying pages:
*Display page by name
.If a page with C<[page some_page]> or C<<>C<A HREF="[area some_page]">C<>> is called and that C<some_page.html> exists in the pages directory (C<PageDir>), it will be displayed.
*On-the-fly page
.If a page with C<[page 00-0011]> or C<<>C<A HREF="[area 00-0011]">C<>> is called and C<00-0011> exists as a product in one of the products databases (C<ProductFiles>), Interchange will use the special page descriptor C<flypage> as a template and build based on that part number. This is partly for convenience; the same thing can be accomplished by calling C<[page your_template 00-0011]> and using the C<[data session arg]> to perform the templating. But there is special logic associated with the C<PageSelectField> configuration attribute to allow pages to be built with varying templates.
*Determine page via form action and variables
.If a form action, in almost all cases the page to display will be determined by the C<mv_nextpage> form value. Example:
!block example; listitem=2
<FORM ACTION="[process]">
<INPUT TYPE=hidden NAME=mv_todo VALUE=return>
<SELECT NAME=mv_nextpage>
<OPTION VALUE=index>Main page
<OPTION VALUE=browse>Product listing
<OPTION VALUE="ord/basket">Shopping cart
</SELECT>
<INPUT TYPE=submit VALUE=Go>
</FORM>
!endblock
.The C<mv_nextpage> dropdown will determine the page the user goes to.
H2: On-the-fly Catalog Pages
If an item is displayed on the search list (or order list) and there is a link to a special page keyed on the item, Interchange will attempt to build the page "on the fly." It will look for the special page flypage.html, which is used as a template for building the page. If C<[item-field fieldname]>, C<[item-price]> and similar elements are used on the page, complex and information-packed pages can be built. The C<[if-item-field fieldname]> HTML C<[/if-item-field]> pair can be used to insert HTML only if there is a non-blank value in a particular field.
{{B:Important note:}} Because the tags are substituted globally on the page, C<[item-*]> tags cannot be used on the default on-the-fly page. To use a [search-region] or [item-list] tag, change the default with the prefix parameter. Example:
!block example
[item-list prefix=cart]
[cart-code] -- title=[cart-data products title]
[/item-list]
!endblock
To have an on-the-fly page mixed in reliably, use the idiom C<[fly-list prefix=fly code="[data session arg]"]> C<[/flylist]> pair.
LI1: [fly-list code="product_code" base="table"] ... [/fly-list]
.Other parameters:
!block example; listitem=2
prefix=label Allows [label-code], [label-description]
!endblock
.Defines an area in a random page which performs the flypage lookup function, implementing the tags below:
!block example; listitem=2
[fly-list code="[data session arg]"]
(contents of flypage.html)
[/fly-list]
!endblock
.If placed around the contents of the demo flypage, in a file named <flypage2.html>, it will make these two calls display identical pages:
!block example; listitem=2
[page 00-0011] One way to display the Mona Lisa </a>
[page flypage2 00-0011] Another way to display the Mona Lisa </a>
!endblock
If the directive PageSelectField is set to a valid product database field which contains a valid Interchange page name (relative to the catalog pages directory, without the .html suffix), it will be used to build the on-the-fly page.
Active tags in their order of interpolation:
!block example
[if-item-field field] Tests for a non-empty, non-zero value in B<field>
[if-item-data db field] Tests for a non-empty, non-zero B<field> in B<db>
[item-code] Product code of the displayed item
[item-accessories args] Accessory information (see I<accessories>)
[item-description] Description field information
[item-price quantity*] Product price (at B<quantity>)
[item-field field] Product database B<field>
[item-data db field] Database B<db> entry for B<field>
!endblock
H2: Special Pages
A number of HTML pages are special for Interchange operation. Typically, they
are used to transmit error messages, status of search or order operations and
other out of boundary conditions.
Note: The distributed demo does not use all of the default values.
The names of these pages can be set with the I<SpecialPage> directive. The standard pages and their default locations:
LI1: canceled (special_pages/canceled.html)
.The page displayed by Interchange when an order has been canceled by the user.
LI1: catalog (special_pages/catalog.html)
.The main catalog page presented by Interchange when another page is not specified.
LI1: failed (special_pages/failed.html)
.If the sendmail program could not be invoked to email the completed order, the failed.html page is displayed.
LI1: flypage (special_pages/flypage.html)
.If the catalog page for an item was not found when its C<[item-link]> is clicked, this page is used as a template to build an on-the-fly page. See On-the-fly Catalog Pages.
LI1: interact (special_pages/interact.html)
.Displayed if an unexpected response was received from the browser, such as not getting expected fields from submitting a form. This would probably happen from typos in the html pages, but could be a browser bug.
LI1: missing (special_pages/missing.html)
.This page is displayed if the URL from the browser specifies a page that does not have a matching .html file in the pages directory. This can happen if the customer saved a bookmark to a page that was later removed from the database, for example, or if there is a defect in the code.
.Essentially this is the same as a 404 error in HTTP. To deliberately display a 404 error, just put this in special_pages/missing.html:
!block example; listitem=2
[tag op=header]Status: 404 missing[/tag]
!endblock
LI1: noproduct (special_pages/noproduct.html)
.This page is displayed if the URL from the browser specifies the ordering of a product code which is not in the products file.
LI1: order (ord/basket.html)
.This page is displayed when the customer orders an item. It can contain any or all of the customer-entered values, but is commonly used as a status display (or "shopping basket").
LI1: search (results.html)
.Contains the default output page for the search engine results. Also required is an input page, which can be the same as search.html or an additional page. By convention Interchange defines this as the page C<results>.
!block example; listitem=2
SpecialPage search results
!endblock
LI1: violation (special pages/violation.html)
.Displayed if a security violation is noted, such as an attempt to access a page denied by an C<access_gate>. See UserDB.
H2: Checking Page HTML
Interchange allows debugging of page HTML with an external page checking program. Because leaving this enabled on a production system is potentially a very bad performance degradation, the program is set in a the global configuration file with the CheckHTML directive. To check a page for validity, set the global directive CheckHTML to the name of the program (don't do any output redirection). A good choice is the freely available program Weblint. It would be set in interchange.cfg with:
!block example
CheckHTML /usr/local/bin/weblint -s -
!endblock
Of course, the server must be restarted for it to be recognized. The full path to the program should be used. If having trouble, check it from the command line (as with all external programs called by Interchange).
Insert C<[flag type=checkhtml][/tag]> at the top or bottom of pages to check and the output of the checker should be appended to the browser output as a comment, visible if the page or frame source are viewed. To do this occasionally, use a Variable setting:
!block example
Variable CHECK_HTML [flag type=checkhtml]
!endblock
and place __CHECK_HTML__ in the pages. Then set the Variable to the empty string to disable it.
H1: Forms and Interchange
Interchange uses HTML forms for many of its functions, including ordering, searching, updating account information and maintaining databases. Order operations possibly include ordering an item, selecting item size or other attributes and reading user information for payment and shipment. Search operations may also be triggered by a form.
Interchange supports file upload with the C<multipart/form-data> type. The file is placed in memory and discarded if not accessed with the C<[value-extended name=filevar file_contents=1]> tag or written with C<[value-extended name=filevar outfile=your_file_name]>. See Extended Value Access and File Upload.
Interchange passes variables from page to page automatically. Every user session that is started by Interchange automatically creates a variable set for the user. As long as the user session is maintained, and does not expire, any variables you set on a form will be "remembered" in future sessions.
Don't use the prefix mv_ for your own variables. Interchange treats these specially and they may not behave as you wish. Use the mv_ variables only as they are documented.
Interchange does not unset variables it does not find on the current form. That means you can't expect a checkbox to become unchecked unless you explicitly reset it.
H2: Special Form Fields
Interchange treats some form fields specially, to link to the search engine and provide more control over user presentation. It has a number of predefined variables, most of whose names are prefixed with C<mv_> to prevent name clashes with your variables. It also uses a few variables which are post-fixed with integer digits; those are used to provide control in its iterating lists.
Most of these special fields begin with C<mv_>, and include:
(O = order, S = search, C = control, A = all, X in scratch space)
!block table; colvaligns="TTTT"
Name|scan|Type|Description
mv_all_chars|ac|S|Turns on punctuation matching
mv_arg[0-9]+||A|Parameters for mv_subroutine (mv_arg0,mv_arg1,...)
mv_base_directory|bd|S|Sets base directory for search file names
mv_begin_string|bs|S|Pattern must match beginning of field
mv_case|cs|S|Turns on case sensitivity
mv_cartname||O|Sets the shopping cart name
mv_check||A|Any form, sets multiple user variables after update
mv_click||A|Any form, sets multiple form variables before update
mv_click||XA|Default mv_click routine, click is mv_click_arg
mv_click <name>||XA|Routine for a click <name>, sends click as arg
mv_click_arg||XA|Argument name in scratch space
mv_coordinate|co|S|Enables field/spec matching coordination
mv_column_op|op|S|Operation for coordinated search
mv_credit_card*||O|Discussed in order security (some are read-only)
mv_dict_end|de|S|Upper bound for binary search
mv_dict_fold|df|S|Non-case sensitive binary search
mv_dict_limit|di|S|Sets upper bound based on character position
mv_dict_look|dl|S|Search specification for binary search
mv_dict_order|do|S|Sets dictionary order mode
mv_doit||A|Sets default action
mv_email||O|Reply-to address for orders
mv_exact_match|em|S|Sets word-matching mode
mv_fail_form||A|Sets CGI values to use on failed profile check
mv_fail_href||A|Sets page to display on on failed profile check
mv_fail_zero||A|Forces zeroing of current form values on failed profile check
mv_field_file|ff|S|Sets file to find field names for Glimpse
mv_field_names|fn|S|Sets field names for search, starting at 1
mv_first_match|fm|S|Start displaying search at specified match
mv_form_profile||A|Check form with Interchange profile
mv_head_skip|hs|S|Sets skipping of header line(s) in index
mv_index_delim|id|S|Delimiter for search fields (TAB default)
mv_matchlimit|ml|S|Sets match page size
mv_max_matches|mm|S|Sets maximum match return
mv_min_string|ms|S|Sets minimum search spec size
mv_negate|ne|S|Records NOT matching will be found
mv_nextpage|np|A|Sets next page user will go to
mv_numeric|nu|S|Comparison numeric in coordinated search
mv_order_group||O|Allows grouping of master item/sub item
mv_order_item||O|Causes the order of an item
mv_order_number||O|Order number of the last order (read-only)
mv_order_quantity||O|Sets the quantity of an ordered item
mv_order_profile||O|Selects the order check profile
mv_order_receipt||O|Sets the receipt displayed
mv_order_report||O|Sets the order report sent
mv_order_subject||O|Sets the subject line of order email
mv_orsearch|os|S|Selects AND/OR of search words
mv_profile|mp|S|Selects search profile
mv_record_delim|dr|S|Search index record delimiter
mv_return_all|ra|S|Return all lines found (subject to range search)
mv_return_delim|rd|S|Return record delimiter
mv_return_fields|rf|S|Fields to return on a search
mv_return_file_name|rn|S|Set return of file name for searches
mv_return_spec|rs|S|Return the search string as the only result
mv_save_session||C|Set to non-zero to prevent expiration of user session
mv_search_field|sf|S|Sets the fields to be searched
mv_search_file|fi|S|Sets the file(s) to be searched
mv_search_line_return|lr|S|Each line is a return code (loop search)
mv_search_match_count||S|Returns the number of matches found (read-only)
mv_search_page|sp|S|Sets the page for search display
mv_searchspec|se|S|Search specification
mv_searchtype|st|S|Sets search type (text, glimpse, db or sql)
mv_separate_items||O|Sets separate order lines (one per item ordered)
mv_session_id|id|A|Suggests user session id (overridden by cookie)
mv_shipmode||O|Sets shipping mode for custom shipping
mv_sort_field|tf|S|Field(s) to sort on
mv_sort_option|to|S|Options for sort
mv_spelling_errors|er|S|Number of spelling errors for Glimpse
mv_substring_match|su|S|Turns off word-matching mode
mv_success_form||A|Sets CGI values to use on successful profile check
mv_success_href||A|Sets page to display on on successful profile check
mv_success_zero||A|Forces zeroing of current form values on successful profile check
mv_todo||A|Common to all forms, sets form action
mv_todo.map||A|Contains form imagemap
mv_todo.checkout.x||O|Causes checkout action on click of image
mv_todo.return.x||O|Causes return action on click of image
mv_todo.submit.x||O|Causes submit action on click of image
mv_todo.x||A|Set by form imagemap
mv_todo.y||A|Set by form imagemap
mv_unique|un|S|Return unique search results only
mv_value|va|S|Sets value on one-click search (va=var=value)
!endblock
H2: Form Actions
Interchange form processing is based on an C<action> and a C<todo>. This can be
gated with C<mv_form_profile> to determine actions and form values based on
a check for required values or other preconditions.
The predefined actions at the first level are:
!block example
process process a todo
search form-based search
scan path-based search
order order an item
!endblock
Any action can be defined with C<ActionMap>.
The C<process> action has a second C<todo> level called with C<mv_todo> or C<mv_doit>. The C<mv_todo> takes preference over C<mv_doit>, which can be used to set a default if no C<mv_todo> is set.
The action can be specified with any of:
LI1: page name
.Calling the page "search" will cause the search action. C<process> will cause a form process action, etc. Examples:
!block example; listitem=2
<FORM ACTION="/cgi-bin/simple/search" METHOD=POST>
<INPUT NAME=mv_searchspec>
</FORM>
!endblock
.The above is a complete search in Interchange. It causes a simple text search of the default products database(s). Normally hard-coded paths are not used, but a Interchange tag can be used to specify it for portability:
!block example; listitem=2
<FORM ACTION="[area search]" METHOD=POST>
<INPUT NAME=mv_searchspec>
</FORM>
!endblock
.The tag C<[process]> is often seen in Interchange forms. The above can be called equivalently with:
!block example; listitem=2
<FORM ACTION="[process]" METHOD=POST>
<INPUT TYPE=hidden NAME=mv_todo VALUE=search>
<INPUT NAME=mv_searchspec>
</FORM>
!endblock
LI1: mv_action
.Setting the special variable C<mv_action> causes the page name to be ignored as the action source. The above forms can use this as a synonym:
!block example; listitem=2
<FORM ACTION="[area foo]" METHOD=post>
<INPUT TYPE=hidden NAME=mv_action VALUE=search>
<INPUT NAME=mv_searchspec>
</FORM>
!endblock
.The page name will be used to set C<mv_nextpage>, if it is not otherwise defined. If C<mv_nextpage> is present in the form, it will be ignored.
The second level C<todo> for the C<process> action has these defined by default:
!block example
back Go to mv_nextpage, don't update variables
search Trigger a search
submit Submit a form for validation (and possibly a final order)
go Go to mv_nextpage (same as return)
return Go to mv_nextpage, update variables
set Update a database table
refresh Go to mv_orderpage|mv_nextpage and check for
ordered items
cancel Erase the user session
!endblock
If a page name is defined as an action with C<ActionMap> or use of Interchange's predefined action C<process>, it will cause form processing. First level is setting the special page name C<process>, or mv_action set to do a form C<process>, the Interchange form can be used for any number of actions. The actions are mapped by the ActionMap directive in the catalog configuration file, and are selected on the form with either the mv_todo or mv_doit variables.
To set a default action for a C<process> form, set the variable C<mv_doit> as a hidden variable:
!block example
<INPUT TYPE=hidden NAME=mv_doit VALUE=refresh>
!endblock
When the mv_todo value is not found, the refresh action defined in mv_doit will be used instead.
More on the defined actions:
LI1: back
.Goes to the page in mv_nextpage. No user variable update.
LI1: cancel
.All user information is erased, and the shopping cart is emptied. The user is then sent to mv_nextpage.
LI1: refresh
.Checks for newly-ordered items in C<mv_order_item>, looking for on-the-fly items if that is defined, then updates the shopping cart with any changed quantities or options. Finally updates the user variables and returns to the page defined in mv_orderpage or mv_nextpage (in that order of preference).
LI1: return
.Updates the user variables and returns to the page defined in mv_nextpage.
LI1: search
.The shopping cart and user variables are updated, then the form variables are interpreted and the search specification contained therein is dispatched to the search engine. Results are returned on the defined search page (set by mv_search_page or the search page directives).
LI1: submit
.Submits the form for order processing. If no order profile is defined with the C<mv_order_profile> variable, the order is checked to see if the current cart contains any items and the order is submitted.
.If there is an order profile defined, the form will be checked against the definition in the order profile and submitted if the pragma &final is set to yes. If C<&final> is set to no (the default) and the check succeeds, the user will be routed to the Interchange page defined in mv_successpage or mv_nextpage. If the check fails, the user will be routed to mv_failpage or mv_nextpage in that order.
H2: Profile checking
Interchange can check forms for compliance with requirements. The mechanism uses
a B<profile>, which is set via the C<mv_form_profile> or C<mv_order_profile>
variables.
H3: mv_form_profile
The C<mv_form_profile> checks happen before any other action. They are designed
to prevent submission of a form if required parameters are not present.
You define the profiles either in scratch space or with files specified
in the I<OrderProfile> directive.
Specifications take the form of an order page variable (like name
or address), followed by an equals sign and a check type. There are many
provided check types, and custom ones can be defined in a user-specified
file using the C<CodeDef> configuration directive.
Standard check types:
=over 4
=item required
A non-blank value is required in the user session space. It may have
been submitted on a previous form.
=item mandatory
Must be non-blank, and must have been specified on this
form, not a saved value from a previous form
=item phone
The field must look like a phone number, by a very
loose specification allowing numbers from all countries
=item phone_us
Must have US phone number formatting, with area code
=item state
Must be a US state, including DC and Puerto Rico.
=item province
Must be a Canadian province or pre-1997 territory.
=item state_province
Must be a US state or Canadian province.
=item zip
Must have US postal code formatting, with optional ZIP+4.
Also called by the alias C<us_postcode>.
=item ca_postcode
Must have Canadian postal code formatting. Checks for a valid
first letter.
=item postcode
Must have Canadian or US postal code formatting.
=item true
Field begins with B<y>, B<1>, or B<t> (Yes, 1, or True) - not case sensitive
=item false
Field begins with B<n>, B<0>, or B<f> (No, 0, or False) - not case sensitive
=item email
Rudimentary email address check, must have an '@' sign,
a name, and a minimal domain
=item regex
One or more regular expressions (space-separated) to check against. To
check that all submissions of the "foo" variable have "bar" at the
beginning, do:
> foo=regex ^bar
You can add an error message by putting it in quotes at the end:
> foo=regex ^bar "You must have bar at the beginning of this"
You can require that the value B<not> match the regex by preceding the regex with a B<!> character (and no space afterwards):
> foo=regex !^bar "You may not have bar at the beginning!"
=item length
A range of lengths you want the input to be:
> foo=length 4-10
That will require C<foo> be from 4 to 10 characters long.
=item unique
Tests to see that the value would be a unique key in a table:
> foo=unique userdb Sorry, that username is already taken
=item filter
Runs the value through an Interchange filter and checks that the returned value is equal
to the original value.
> foo=filter entities Sorry, no HTML allowed
To check for all lower-case characters:
> foo=filter lower Sorry, no uppercase characters
=back
Also, there are pragmas that can be used to change behavior:
=over 4
=item &charge
Perform a real-time charge operation. If set to any value but
"custom", it will use Interchange's CyberCash routines. To set to
something else, use the value "custom ROUTINE". The ROUTINE should
be a GlobalSub which will cause the charge operation to occur -- if
it returns non-blank, non-zero the profile will have succeeded. If
it returns 0 or undef or blank, the profile will return failure.
=item &credit_card
Checks the mv_credit_card_* variables for validity. If set to
"standard", it will use Interchange's C<encrypt_standard_cc> routines.
This destroys the CGI value of mv_credit_card_number -- if you don't
want that to happen (perhaps to save it for sending to CyberCash)
then add the word C<keep> on the end.
\Example:
> # Checks credit card number and destroys number after encryption
> # The charge operation can never work
>
> &credit_card=standard
> &charge=custom authorizenet
>
> # Checks credit card number and keeps number after encryption
> # The charge operation can now work
>
> &credit_card=standard keep
> &charge=custom authorizenet
You can supply your own check routine with a GlobalSub:
> &credit_card=check_cc
The C<GlobalSub> check_cc will be used to check and encrypt the
credit card number, and its return value will be used to determine
profile success.
=item C<&>fail
Sets the mv_nextpage value for a failed check.
> &fail=page4
If the submit process succeeds, the user will be sent to the
page C<page4>.
=item C<&>fatal
Set to '&fatal=yes' if an error should generate
the error page.
=item C<&>final
Set to '&final=yes' if a successful check should cause the order to be placed.
=item C<&>update
Set to '&update=yes' if a successful check should cause the variable to be copied from the CGI space to the Values space. This is like [update values] except only
for that variable.
This is typically used when using a C<mv_form_profile> check so that
a failing check will not cause all values to be reset to their former
state upon returning to the form.
=item C<&>return
Causes profile processing to terminate with either a success
or failure depending on what follows. If it is non-blank and
non-zero, the profile succeeds.
> # Success :)
> &return 1
>
> # Failure :\
> &return 0
Will ignore the &fatal pragma, but &final is still in effect if set.
=item C<&>set
Set a user session variable to a value, i.e. C<&set=mv_email [value email]>.
This will not cause failure if blank or zero.
=item C<&>setcheck
Set a user session variable to a value, i.e. C<&set=mv_email [value
email]>. This B<will> cause failure if set to a blank or zero. It is
usually placed at the end after a &fatal pragma would have caused the
process to stop if there was an error -- can also be used to determine
pass/fail based on a derived value, as it will cause failure if it
evaluates to zero or a blank value.
=item C<&>success
Sets the mv_nextpage value if the profile succeeds. Example:
> &success=page5
If the submit process succeeds, the user will be sent to the
page C<page5>.
=item C<&>update
Normally if an mv_form_profile check fails none of the form values
are put in the user session, meaning that the C<[value ...]> tag will
not reflect what the user has submitted. If you set C<&update=yes>,
then as each variable is checked its value will be updated. Example:
> &update=yes
Even if the profile check fails, any variables checked before the &fatal=yes
setting will be updated.
=back
As an added measure of control, the specification is evaluated for the
special Interchange tags to provide conditional setting of order
parameters. With the C<[perl]> C<[/perl]> capability, quite complex checks
can be done. Also, the name of the page to be displayed on an error can
be set in the C<mv_failpage> variable.
Error messages are set by appending the desired error message to the
line containing the check:
city=required Please fill in your city.
This sets the value of the error associated with name, and can be displayed
with the C<error> tag:
!block example
[error name=city show_error=1]
!endblock
H2: Profile examples
The following file specifies a simple check of formatted parameters:
> name=required You must give us your name.
> address=required Oops! No address.
> city=required
> state=required
> zip=required
> email=required
> phone_day=phone_us XXX-XXX-XXXX phone-number for US or Canada
> &fatal=yes
> email=email Email address missing the domain?
> &success=ord/shipping
The profile above only performs the &success directive if all of the
previous checks have passed -- the &fatal=yes will stop processing after
the check of the email address if any of the previous checks failed.
If you want to place multiple order profiles in the same file,
separate them with __END__, which must be on a line by itself.
H2: User defined check routines
User-defined check routines can be defined with CodeDef in a file
included in the code/ directory, the same as a C<UserTag>.
> CodeDef foo OrderCheck
> CodeDef foo Routine <<EOR
> sub {
> # $ref is to Vend::Session->{'values'} hash
> # $var is the passed name of the variable
> # $val is current value of checked variable
> my($ref, $var, $val) = @_;
>
> my($ref, $var, $val) = @_;
>
> if($val ne 'bar') {
> return (undef, $var, "The value of foo must be bar");
> }
> else {
> return (1, $var, '');
> }
> }
> EOF
Now you can specify in an order profile:
> foo_variable=foo
Very elaborate checks are possible. The return value of the subroutine
should be a three-element array, consisting of:
^ the pass/fail ('1' or 'undef') status of the check;
+ the name of the variable which was checked;
+ a standard error message for the failure, in case a custom one has
not been specified in the order profile.
The latter two elements are used by the C<[error]> tag for on-screen
display of form errors. The checkout page of the Foundation demo includes
examples of this.
H2: One-click Multiple Variables
Interchange can set multiple variables with a single button or form control. First define the variable set (or profile, as in search and order profiles) inside a scratch variable:
!block example
[set Search by Category]
mv_search_field=category
mv_search_file=categories
mv_todo=search
[/set]
!endblock
The special variable C<mv_click> sets variables just as if they were put in on the form. It is controlled by a single button, as in:
!block example
<INPUT TYPE=submit NAME=mv_click VALUE="Search by Category">
!endblock
When the user clicks the submit button, all three variables will take on the values defined in the "Search by Category" scratch variable. Set the scratch variable on the same form as the button is on. This is recommended for clarity.
The C<mv_click> variable will not be carried from form to form, it must be set
on the form being submitted.
The special variable C<mv_check> sets variables for the form actions <checkout, control, refresh, return, search,> and <submit>. This function operates after the values are set from the form, including the ones set by C<mv_click>, and can be used to condition input to search routines or orders.
The variable sets can contain and be generated by most Interchange tags. The profile is interpolated for Interchange tags before being used. This may not always operate as expected. For instance, if the following was set:
!block example
[set check]
[cgi name=mv_todo set=bar hide=1]
mv_todo=search
[if cgi mv_todo eq 'search']
do something
[/if]
[/set]
!endblock
The if condition is guaranteed to be false, because the tag interpretation
takes place before the evaluation of the variable setting.
Any setting of variables already containing a value will overwrite the variable. To build sets of fields (as in mv_search_field and mv_return_fields), comma separation if that is supported for the field must be used.
It is very convenient to use C<mv_click> as a trigger for embedded Perl:
!block example
<FORM ...
<INPUT TYPE=hidden NAME=mv_check VALUE="Invalid Input">
...
</FORM>
[set Invalid Input]
[perl]
my $type = $CGI->{mv_searchtype};
my $spell_check = $CGI->{mv_spelling_errors};
my $out = '';
if($spell_check and $type eq 'text') {
$CGI->{mv_todo} = 'return';
$CGI->{mv_nextpage} = 'special/cannot_spell_check';
}
return;
[/perl]
[/set]
!endblock
H2: Checks and Selections
A "memory" for drop-down menus, radio buttons and checkboxes can be provided with the C<[checked]> and C<[selected]> tags.
LI1: [checked var_name value]
.named attributes: C<[checked name="var_name" value="value" cgi=0|1 multiple=0|1 default=0|1 case=0|1]>
.This will output CHECKED if the variable C<var_name> is equal to C<value>. Set the C<cgi> attribute to use cgi instead of values data. Not case sensitive unless C<case> is set.
.If the C<multiple> attribute is defined and set to a non-zero value (1 is implicit) and if the value matches on a word/non-word boundary, it will be CHECKED. If the C<default> attribute is set to a non-zero value, the box will be checked if the variable C<var_name> is empty or zero.
LI1: [selected var_name value]
.named attributes: C<[selected name="var_name" value="value" cgi=0|1 multiple=0|1 default=0|1 case=0|1]>
.This will output SELECTED if the variable C<var_name> is equal to C<value>. Set the C<cgi> attribute to use cgi instead of values data. Not case sensitive unless C<case> is set.
.If the C<multiple> argument is present, it will look for any of a variety of values. If the C<default> attribute is set, SELECT will be output if the variable is empty or zero. Not case sensitive unless C<case> is set.
.Here is a drop-down menu that remembers an item-modifier color selection:
!block example; listitem=2
<SELECT NAME="color">
<OPTION [selected name=color value=blue]> Blue
<OPTION [selected name=color value=green]> Green
<OPTION [selected name=color value=red]> Red
</SELECT>
!endblock
.For databases or large lists of items, sometimes it is easier to use C<[loop list="foo bar"]> and its C<option> parameter. The above can be achieved with:
!block example; listitem=2
<SELECT NAME=color>
[loop list="Blue Green Red" option=color]
<OPTION> [loop-code]
[/loop]
</SELECT>
!endblock
See also the C<ictags> documentation on the [loop] tag.
H2: Integrated Image Maps
Imagemaps can also be defined on forms, with the special form variable C<mv_todo.map>. A series of map actions can be defined. The action specified in the I<default> entry will be applied if none of the other coordinates match. The image is specified with a standard HTML 2.0 form field of type IMAGE. Here is an example:
!block example
<INPUT TYPE=hidden NAME="mv_todo.map" VALUE="rect submit 0,0 100,20">
<INPUT TYPE=hidden NAME="mv_todo.map" VALUE="rect cancel 290,2 342,18">
<INPUT TYPE=hidden NAME="mv_todo.map" VALUE="default refresh">
<INPUT TYPE=image NAME="mv_todo" SRC="url_of_image">
!endblock
All of the actions will be combined together into one image map with NCSA-style functionality (see the NCSA imagemap documentation for details), except that Interchange form actions are defined instead of URLs.
H2: Setting Form Security
You can cause a form to be submitted securely (to the base URL in the SecureURL directive, that is) by specifying your form input to be ACTION="[process secure=1]".
To submit a form to the regular non-secure server, just omit the C<secure> modifier.
H2: Stacking Variables on the Form
Many Interchange variables can be "stacked," meaning they can have multiple values for the same variable name. As an example, to allow the user to order multiple items with one click, set up a form like this:
!block example
<FORM METHOD=POST ACTION="[process]">
<input type=checkbox name="mv_order_item" value="M3243"> Item M3243
<input type=checkbox name="mv_order_item" value="M3244"> Item M3244
<input type=checkbox name="mv_order_item" value="M3245"> Item M3245
<input type=hidden name="mv_doit" value="refresh">
<input type=submit name="mv_junk" value="Order Checked Items">
</FORM>
!endblock
The stackable C<mv_order_item> variable with be decoded with multiple values, causing the order of any items that are checked.
To place a "delete" checkbox on the shopping basket display:
!block example
<FORM METHOD=POST ACTION="[process]">
[item-list]
<input type=checkbox name="[quantity-name]" value="0"> Delete
Part number: [item-code]
Quantity: <input type=text name="[quantity-name]" value="[item-quantity]">
Description: [item-description]
[/item-list]
<input type=hidden name="mv_doit" value="refresh">
<input type=submit name="mv_junk" value="Order Checked Items">
</FORM>
!endblock
In this case, first instance of the variable name set by C<[quantity-name]> will be used as the order quantity, deleting the item from the form.
Of course, not all variables are stackable. Check the documentation for which ones can be stacked or experiment.
H2: Extended Value Access and File Upload
Interchange has a facility for greater control over the display of form variables; it also can parse C<multipart/form-data> forms for file upload.
File upload is simple. Define a form like:
!block example
<FORM ACTION="[process-target] METHOD=POST ENCTYPE="multipart/form-data">
<INPUT TYPE=hidden NAME=mv_todo VALUE=return>
<INPUT TYPE=hidden NAME=mv_nextpage VALUE=test>
<INPUT TYPE=file NAME=newfile>
<INPUT TYPE=submit VALUE="Go!">
</FORM>
!endblock
The [value-extended ...] tag performs the fetch and storage of the file. If the following is on the C<test.html> page (as specified with C<mv_nextpage> and used with the above form, it will write the file specified:
!block example
<PRE>
Uploaded file name: [value-extended name=newfile]
Is newfile a file? [value-extended name=newfile yes=Yes no=No test=isfile]
Write the file. [value-extended name=newfile outfile=junk.upload]
Write again with
indication: [value-extended name=newfile
outfile=junk.upload
yes="Written."]
no=FAILED]
And the file contents:
[value-extended name=newfile file_contents=1]
</PRE>
!endblock
The [value-extended] tag also allows access to the array values of stacked variables. Use the following form:
!block example
<FORM ACTION="[process-target] METHOD=POST ENCTYPE="multipart/form-data">
<INPUT TYPE=hidden NAME=testvar VALUE="value0">
<INPUT TYPE=hidden NAME=testvar VALUE="value1">
<INPUT TYPE=hidden NAME=testvar VALUE="value2">
<INPUT TYPE=submit VALUE="Go!">
</FORM>
!endblock
and page:
!block example
testvar element 0: [value-extended name=testvar index=0]
testvar element 1: [value-extended name=testvar index=1]
testvar elements:
joined with a space: |[value-extended name=testvar]|
joined with a newline: |[value-extended
joiner="\n"
name=testvar
index="*"]|
first two only: |[value-extended
name=testvar
index="0..1"]|
first and last: |[value-extended
name=testvar
index="0,2"]|
!endblock
to observe this in action.
The syntax for C<[value-extended ...]> is:
!block example
named: [value-extended
name=formfield
outfile=filename*
ascii=1*
yes="Yes"*
no="No"*
joiner="char|string"*
test="isfile|length|defined"*
index="N|N..N|*"
file_contents=1*
elements=1*]
!endblock
positional: [value-extended name]
Expands into the current value of the customer/form input field named by field. If there are multiple elements of that variable, it will return the value at C<index>; by default all joined together with a space.
If the variable is a file variable coming from a multipart/form-data file upload, then the contents of that upload can be returned to the page or optionally written to the C<outfile>.
LI1: name
.The form variable NAME. If no other parameters are present, the value of the variable will be returned. If there are multiple elements, by default they will all be returned joined by a space. If C<joiner> is present, they will be joined by its value.
.In the special case of a file upload, the value returned is the name of the file as passed for upload.
LI1: joiner
.The character or string that will join the elements of the array. It will accept string literals such as "\n" or "\r".
LI1: test
.There are three tests. C<isfile> returns true if the variable is a file upload. C<length> returns the length. C<defined> returns whether the value has ever been set at all on a form.
LI1: index
.The index of the element to return if not all are wanted. This is useful especially for pre-setting multiple search variables. If set to C<*>, it will return all (joined by C<joiner>). If a range, such as C<0 .. 2>, it will return multiple elements.
LI1: file_contents
.Returns the contents of a file upload if set to a non-blank, non-zero value. If the variable is not a file, it returns nothing.
LI1: outfile
.Names a file to write the contents of a file upload to. It will not accept an absolute file name; the name must be relative to the catalog directory. If images or other files are to be written to go to HTML space, use the HTTP server's C<Alias> facilities or make a symbolic link.
LI1: ascii
.To do an auto-ASCII translation before writing the C<outfile>, set the C<ascii> parameter to a non-blank, non-zero value. The default is no translation.
LI1: yes
.The value that will be returned if a test is true or a file is written successfully. It defaults to C<1> for tests and the empty string for uploads.
LI1: no
.The value that will be returned if a test is false or a file write fails. It defaults to the empty string.
H2: Updating Interchange Database Tables with a Form
Any Interchange database can be updated with a form using the following method.
The Interchange user interface uses this facility extensively.
Note: All operations are performed on the database, not the ASCII source file. An [export table_name] operation will have to be performed for the ASCII source file to reflect the results of the update. Records in any database may be inserted or updated with the [query] tag, but form-based updates or inserts may also be performed.
In an update form, special Interchange variables are used to select the database parameters:
LI1: mv_data_enable (scratch)
.\IMPORTANT: This must be set to a non-zero, non-blank value in the scratch space to allow data set functions. Usually it is put in an mv_click that precedes the data set function. For example:
!block example; listitem=2
[set update_database]
[if type=data term="userdb::trusted::[data session username]"]
[set mv_data_enable]1[/set]
[else]
[set mv_data_enable]0[/set]
[/else]
[/if]
[/set]
<INPUT TYPE=hidden NAME=mv_click VALUE=update_database>
!endblock
LI1: mv_data_table
.The table to update.
LI1: mv_data_key
.The field that is the primary key in the table. It must match the existing database definition.
LI1: mv_data_function
.UPDATE, INSERT or DELETE. The variable C<mv_data_verify> must be set true on the form for a DELETE to occur.
LI1: mv_data_verify
.Confirms a DELETE.
LI1: mv_data_fields
.Fields from the form which should be inserted or updated. Must be existing columns in the table in question.
LI1: mv_update_empty
.Normally a variable that is blank will not replace the field. If C<mv_update_empty> is set to true, a blank value will erase the field in the database.
LI1: mv_data_filter_(field)
.Instantiates a filter for C<(field)>, using any of the defined Interchange filters. For example, if C<mv_data_filter_foo> is set to C<digits>, only digits will be passed into the database field during the set operation. A common value might be "entities", which protects any HTML by translating C<<> into C<&lt;>, C<"> into C<&quot;>, etc.
The Interchange action set causes the update. Here are a pair of example forms. One is used to set the key to access the record (careful with the name, this one goes into the user session values). The second actually performs the update. It uses the C<[loop]> tag with only one value to place default/existing values in the form based on the input from the first form:
!block example
<FORM METHOD=POST ACTION="[process]">
<INPUT TYPE=HIDDEN name="mv_doit" value="return">
<INPUT TYPE=HIDDEN name="mv_nextpage" value="update_proj">
Sales Order Number <INPUT TYPE=TEXT SIZE=8
NAME="update_code"
VALUE="[value update_code]">
<INPUT TYPE=SUBMIT name="mv_submit" Value="Select">
</FORM>
!endblock
!block example
<FORM METHOD=POST ACTION="[process]">
<INPUT TYPE=HIDDEN NAME="mv_data_table" VALUE="ship_status">
<INPUT TYPE=HIDDEN NAME="mv_data_key" VALUE="code">
<INPUT TYPE=HIDDEN NAME="mv_data_function" VALUE="update">
<INPUT TYPE=HIDDEN NAME="mv_nextpage" VALUE="updated">
<INPUT TYPE=HIDDEN NAME="mv_data_fields"
VALUE="code,custid,comments,status">
<PRE>
[loop arg="[value update_code]"]
Sales Order <INPUT TYPE=TEXT NAME="code SIZE=10 VALUE="[loop-code]">
Customer No. <INPUT TYPE=TEXT NAME="custid" SIZE=30
VALUE="[loop-field custid]">
Comments <INPUT TYPE=TEXT NAME="comments"
SIZE=30 VALUE="[loop-field comments]">
Status <INPUT TYPE=TEXT NAME="status"
SIZE=10 VALUE="[loop-field status]">
[/loop]
</PRE>
<INPUT TYPE=hidden NAME="mv_todo" VALUE="set">
<INPUT TYPE=submit VALUE="Update table">
</FORM>
!endblock
The variables in the form do not update the user's session values, so they can correspond to database field names without fear of corrupting the user session.
H3: Can I use Interchange with my existing static catalog pages?
Yes, but you probably won't want to in the long run. Interchange is designed to build pages based on templates from a database. If all you want is a shopping cart, you can mix standard static pages with Interchange, but it is not as convenient and doesn't take advantage of the many dynamic features Interchange offers.
That being said, all you usually have to do to place an order link on a page is:
!block example
<A HREF="/cgi-bin/construct/order?mv_order_item=SKU_OF_ITEM">Order!</A>
!endblock
Replace C</cgi-bin/construct> with the path to your Interchange link.
Line:
N:Copyright 2002-2004 Interchange Development Group. Copyright 2001-2002 Red Hat, Inc. Freely redistributable under terms of the GNU General Public License.
Jump to Line
Something went wrong with that request. Please try again.