Interchange Documentation: Glossary


Table of Contents

AVS
ActionMap
ad
affiliate
anchor
array
attribute
autoload
boolean
CATROOT
CGI
CSS
CyberCash
captcha
cart
Multiple Carts
Debugging
catalog
checkout
Simple Order Report File
Fully-configurable Order Reports
Order Counter
Custom Form Fields
configuration
Configuration directives and syntax
Parsing rules
Subroutine watches
cookie
crypt
DOCROOT
DSN
DocumentRoot
database
Text Source Files
SQL Databases
File-based Databases
Memory Databases
Interchange Database Design
Interchange Database Conventions
debug
dereference
discount
dispatch
environment
epoch
escape
expire
external
false
feature
filter
flypage
form action
form processor
GET
gate
gateway
Gateway Configuration
Multiple Gateways
Transaction Types
Payment Modules
AuthorizeNet
ECHO
Getitcard
LinkPoint
Netbilling
Sage
Signio
TCLink
TestPayment
HTML
hash
hello world
ICROOT
ISBN
ITL
Basics
Standard Syntax
Named and Positional Parameters
HTML Comments
Argument Interpolation
Parsing Order
Deeper Look at Argument Quoting
Accessing Perl from ITL
Universal Attributes
Tag-specific Attributes
Attribute Arrays and Hashes
Looping Tags and Sub-tags
ic run mode
img
internationalization
interpolate
interval
jobs
Directories and Files
Output and Logging
Tracking
js
levy
link
link program
UNIX-Domain Sockets
INET-Domain Sockets
Setting up VLINK and TLINK
Compiling VLINK and TLINK
Placing VLINK and TLINK to proper system locations
VLINK or TLINK compile problems
locale
Localized Catalogs
Locale-specific Messages and Locale Database
Effect of Locales on Sorting
MIME
Minivend
macro
mode
modifier
mv
namespace
OS
onfly
order
Placing Items in the Basket
Items created "on fly"
Order Groups
Basket display
Multiple Shopping Carts
order check
pragma
price
Simple Pricing
Custom Pricing
"CommonAdjust" Pricing
CommonAdjust Strings, Atoms and Settors
Product discounts
profile
Form Profiles
reconfigure
regexp
reparse
robot
rotated banner
SDBM
SKU
SMTP
SOAP RPC
Basic setup
Advanced setup
Faults
SOAP references
SQL
SSL
safe
salt
scalar
scratch
session
shipping
special
static
tab delimited
table
tarball
tax
Simple "salestax.asc" Method
"Fly Tax" Method
"Salestax Multi" or "VAT Taxing" Method
"Levies" or "Multiple Tax" Method
Conclusion
time
tracking
true
UI
UserDB
Creating the user database
Registering the database
Creating the login page
Conclusion
umask
usertag
value
variable
weighted
widget

List of Tables

1. Recognized format strings for time functions

AVS

The Address Verification System (AVS) is used to check the likeliness that the customer claiming to own the credit card is the real owner.

ActionMap

Interchange allows you to create "virtual pages" by associating code (such as Perl subroutines or ITL tags) with parts of an URL. This can be used for anything from implementing one-click searches and orders to displaying on-the-fly data.

On every catalog access, the leading part of a requested URL is taken and checked if it represents a valid action. If it does, the action is invoked with the rest of the arguments provided in the URL.

Some of the predefined actions (which you might recognize from special page names that you access in your catalog) are:

  • process - Generic form processing function

  • order - Order items

  • scan - Search based on submitted URL

  • search - Search based on submitted form variables

ad

A rectangular graphic image which is included on a content page for advertising and promotional purposes.

In Interchange, when "banner" or "ad" is used to describe the functionality of the banner tag and it does not necessarily mean an image, as you can put anything in the content placeholder. In fact, banner examples from ???? HOW-TO use plain text.

Voluntary ad unit guidelines as suggested by the Interactive Advertising Bureau (specified in pixels, in ascending order):

  • Micro Bar - 88x31

  • Button 2 - 120x60

  • Button 1 - 120x90

  • Half Banner - 234x60

  • Square Button - 125x125

  • Full Banner - 468x60

  • Vertical Banner - 120x240

  • Rectangle - 180x150

  • Medium Rectangle - 300x250

  • Vertical Rectangle - 240x400

  • Square Pop-up - 250x250

  • Large Rectangle - 336x280

  • Skyscraper - 120x600

  • Wide Skyscraper - 160x600

You can see the above dimensions in practice at MOTIVE Resources.

affiliate

When giving links to your website to your partners, often times you want to embed an identification string that will link visits or eventual sales to the corresponding partner.

Interchange supports affiliate tracking; the source of the visit or order is always accessible using [data session source].

So when giving links to your affiliates, make sure the affiliate code you assign them is embedded in one of the following ways:

http://myhost.mydomain.local/cgi-bin/standard/index?;;thesource         (old Minivend 3 way, still works)
http://myhost.mydomain.local/cgi-bin/standard/index?mv_pc=thesource     (Interchange way, still works)
http://myhost.mydomain.local/cgi-bin/standard/index?mv_source=thesource (current way)

Note that mv_source was added as an affiliate code-passing option only in Interchange 4.9.x. Before that, affiliate code was passed in mv_pc. The mv_pc variable is used as a variable containing a random number to prevent caching of pages. So if you want to use mv_pc as a way of carrying over the affiliate code, it must contain at least one non-digit. (mv_pc=A12 is a good affiliate code, mv_pc=12 is not). If using mv_source, affiliate code is free of this restriction.

Sometimes you want the affiliate code to disappear from the URL after the visitors get redirected to your page. To enable that feature, see BounceReferrals configuration directive.

anchor

Anchor resource at W3C.

array

In general terms, "array" can be considered a synonym for "list". It implies a list of elements.

In the Perl programming language, "list" refers to an unnamed list of any kind, while "array" refers to a practical, named Perl variable of list type.

attribute

Attributes (sometimes also called modifiers, options or params) are various "sub-features" of a product. If you are selling t-shirts in different colors and sizes, color and size are ideal candidates for item attributes. Interchange allows attributes to be set for each ordered item. This allows a varying size, color, or any other modifier to be attached to an otherwise common part number.

See UseModifier for more information and concrete examples.

Besides setting modifier names in the config files (via the above UseModifier), you can also set them as scratch variables with mv_UseModifier. For example, the above modifiers would be set with [set mv_UseModifier]size color[/set]. This effectively allows you to have different product options for different or even same product SKUs. Those specified in mv_UseModifier at the time of order will be used (just be careful, because you cannot set it more than once on the same page).

[Note]Note

When choosing modifier names, do not use anything that begins with mv_ nor words like code, quantity or group; those variables have already been used up by Interchange itself.

You also need to make sure that no fields in your HTML forms have digits appended to their names, if their non-digit name part is equal to any used attribute. (This is because Interchange treats say, size0 and size1 as multiple size modifier values.)

In addition, setting SeparateItems or mv_separate_items places each ordered item on a separate line even if they have the same SKU, simplifying attribute handling.

The modifier value is accessed in the item-list loop with the [item-modifier attribute_name] tag, and form input fields are created with [modifier-name attribute]. This is similar to the way that quantity is handled, except that attributes can be "stacked" by setting multiple values in an input form (whereas there can be only one quantity field for each item).

When you want to provide a series of modifiers for an element, you can use the standard loop tag (such as [loop arg="item item item"]), or you can use the built-in PREFIX-accessories tag available with most Interchange list operations. The modifier value can then be used to select data from an arbitrary database for attribute selection and display.

Below is a fragment from a shopping basket display form which shows a selectable size with "sticky" setting. Note that the example can only work within the item-list tag.

  <select name="[modifier-name size]">
  <option [selected [modifier-name size] S]  /> S
  <option [selected [modifier-name size] M]  /> M
  <option [selected [modifier-name size] L]  /> L
  <option [selected [modifier-name size] XL] /> XL
  </select>

It could just as easily be done with a radio button group as well (when you combine them with the <checked> HTML tag).

In addition, Interchange would automatically generate the above select box if you called [accessories code size] or [item-accessories size].

autoload

boolean

A boolean variable is one that can only represent a true or false value.

The variable type was named after George Boole.

Note that, in Interchange configuration directives parlance, boolean is used somewhat awkwardly. Instead of adhering to above definition of boolean, it actually signifies an array in which you can search for the presence or absence of a value. Real boolean variables are called yesnos in Interchange.

This naming confusion is unfortunate, but is fortunately quarantined to the configuration directives "space".

CATROOT

Catalog root directory as specified with the Catalog directive.

CGI

The HTTP (Web) protocol does not use the same mechanism to send data from server to client, and from client to server. Client to server communication must usually happen over CGI (Common Gateway Interface), by having users submit HTML forms.

Form data submitted usually consists of key=value pairs. One other option are just values following one another (value1+value2+value3...); those are called "ISINDEX" queries, and are not generally used with Interchange.

Form submission can happen in two ways.

The "GET" method is very basic, as it just embeds form values in the URL being sent to the server. One example of a GET query is http://myhost.mydomain.local/cgi-bin/ic/test?mv_arg=1&mv_pc=14. I think it's simple enough to notice variables mv_arg and mv_pc being submitted. The GET method is very convenient because all data is embedded in the URL, making it very easy to copy and share links with other people.

It is recommended to create these links in Interchange with area.,e.g.:


<a href="[area href="" form="param1=foo
  param2=bar"]">FOO & BAR</a>
</a>

The other method is called POST. This way, the information is sent in a way not visible to the user. POST forms have this disadvantage of not being suitable for copy-pasting HTML links directly, but they do offer greater flexibility, especially if a lot of form data is being sent.

When forms are submitted using the POST method, they can also embed data in the URL, effectively passing both POST and GET data at once. Interchange ignores GET data on POST forms, but can be instructed to parse both using the TolerateGet directive.

In the end, it turns out you can just use GET in most situations. It's simpler, more convenient, and gets the job done just as well.

CGI variables in Interchange are accessible using the cgi tag, and only on a page directly following the form submission. This is logical, of course. A page request reaches the Interchange daemon, and it either has or doesn't have the accompanying form data; there's no "history" mechanism included. (However, Interchange does allow you to save values for future reference, usually in the value or scratch space). Interchange is, by default, eager to collect user information, at least for the duration of the session (so the users don't have to retype it again). During processing, CGI variables are therefore propagated to the values space, for subsequent requests. The FormIgnore directive specifies which CGI variables should not be propagated.

Users have complete control over CGI data they will send. Therefore, this input should never be trusted. It's raw data, and it is a security risk to save it in a database or display in a page before sanitization. The most common security risk is displaying HTML code which allows remote scripting exploits like cookie-stealing.

Never do something like the following:

[cgi VARNAME]

or

[calc]
  my $out = $CGI->{VARNAME};
  return $out;
[/calc]

Fortunately, Interchange offers a number of ways to take care of the data, usually by filtering it. For more discussion and help on filtering, see the filter glossary entry. A safe no-brainer approach is to just use the <filter>encode_entities</filter> filter on the input.

So, to obtain a "safe" value while keeping the original intact, use:

[cgi name=VARNAME filter=entities]

or:

[filter entities][cgi VARNAME][/filter]

or:

[calc]
  my $out = $Tag->cgi({ name => 'VARNAME', filter => 'entities' });
  return $out;
[/calc]

or:

[calc]
  my $out = $Tag->filter($CGI->VARNAME, 'entities');
  return $out;
[/calc]

One interesting feature in Interchange is that you can set CGI values yourself. This has two common uses. You can set a value and pretend as if it was sent by the user (so the rest of your code doesn't need to split in two execution paths, depending on whether the variable was set or not). Another thing you can do, is set special CGI variables (the mv_* ones that affect how Interchange processes the page) and let Interchange do its magic. Heck, not only you can set them once, but you can change their value during processing, achieving different behavior in different parts of the page.

You can set values by providing set=VALUE hide=1 attributes to the cgi tag, or by simple assignment in Perl ($CGI->{VARNAME} = 'VALUE').

Here's a complete list of ways to access CGI variables:

In ITL:

Access syntaxNotes
[cgi VARNAME]Doesn't prevent users from injecting ITL code; don't use it!
[cgi name=VARNAME filter=entities]A safe and correct way to go

In embedded Perl:

Access syntaxNotes
$CGI->{VARNAME}Retrieves raw CGI value; don't use before filtering
$Tag->cgi({ name => 'VARNAME', filter => 'entities' });A safe and correct way to go
$Tag->filter($CGI->{VARNAME}, 'entities');A safe and correct way to go

CSS

CSS resource at W3C.

CyberCash

The CyberCash payment module was removed form Interchange in the 5.7 series.

captcha

cart

Each item in the cart consists of the following fields:

  • code

  • quantity

  • mv_ip

  • mv_mi

  • mv_si

Additional fields can be added with the UseModifier and AutoModifier configuration directives.

Multiple Carts

Debugging

dump The content of all carts ....

catalog

A catalog is the basic functional unit in Interchange. A catalog is to Interchange what a web site is to a Web server.

Catalogs to configure and offer on the Interchange server are defined in the global configuration file, interchange.cfg (or some of the files it includes, of course, depending on the actual file layout). The definition directive is called Catalog. The directive you should use to register a catalog is — incidentally — Catalog.

Each catalog directory (specified as one of Catalog parameters) must have the file catalog.cfg in it. For the mandatory directives that need to be present in the file, see Catalog reference page.

For the general syntax accepted in configuration files, see configuration glossary entry.

For the list of available configuration directives, see Interchange Reference Pages: Configuration directives.

checkout

The check-out process consists of users filling in information via HTML forms, and Interchange verifying their input on arbitrary number of levels using so-called profiles.

Profiles can be defined in external files (and activated using OrderProfile) or in scratch variables. External files are, by convention, kept in CATROOT/etc/ and begin with profiles.. Multiple profiles can be defined in each file.

You can learn about the principle and syntax of the profile files in the profile glossary entry. Only when the input "passes" the profile check, is the check-out process able to proceed.

Simple Order Report File

Most of the time, you will want the successful check-out operation (order completion) to generate some kind of notification. In most common setups, this will include e-mailing order reports.

Simple order report file, CATROOT/etc/report, defines the layout (template) of the order report. All form variables are accessible from the report file by using the familiar Perl $ syntax.

         Order Date: $date
  
               Name: $name
      Email address: $email
  
   Shipping address: $addr
   Town, State, Zip: $town, $state $zip
            Country: $country

Fully-configurable Order Reports

You can specify fully-configurable order reports by setting the hidden form variable mv_order_report to an existing Interchange catalog page. This page will be processed (interpolated and all) as standard Interchange page before being sent by email. That said, you see you could include HTML in the file. Although many mail clients will parse HTML, it seems that the initial excitement among the ordinary people vanished and they again prefer plain-text e-mails. If you wanted to provide a HTML version, you could always provide a link to a copy on your web server.

Order Counter

An order counter can be enabled simply — just set the OrderCounter directive to the appropriate file name. An incrementing count of all orders will be kept and assigned as orders are placed. By default, the number starts at 0, but you can edit the file and change the starting or current number at any time. This feature is made possible by the File::CounterFile Perl module.

Custom Form Fields

The default basket and order pages contain a number of form fields, allowing customers to enter the necessary information. This, however, can't satisfy all individual needs. To remove some of the fields, simply delete them from the HTML pages (or, better yet, disable by using the comment tag). Do not forget to also deactivate any entries in the profile files.

To add new fields, simply add them to the pages. The information will automatically be included in the report files. Here's a template you could re-use for your own fields, replacing town with your values:

<input type="text" name="town" value="[value town]" size="30" maxlen="40" />

[Note]Note

Using maxlen HTML <input> attribute to limit the length of incoming input is insecure, since that check is performed client-side only. It is surely an element of good programming on all levels, but don't forget to perform real length check in the appropriate form profile.

configuration

Interchange supports multiple catalogs, and therefore splits its configuration into two pieces. One is global, specified in interchange.cfg, and affects every catalog running under the same Interchange server installation. The other — catalog part, is specified in each catalog's CATROOT/catalog.cfg, and has no effect on other catalogs.

Each configuration directive is accessible on global or catalog level, or both. There's a special field named "DIRECTIVE TYPE" present in each directive's reference page, where you can look this up. Keep in mind, however, that the directives on global and catalog level don't have to be parsed by the same code — in fact, they're mostly parsed by related but different code blocks.

Configuration directives and syntax

Configuration directives are normally specified with the directive name as the first word on the line, with its value or values following. Capitalization of the directive name is not significant, but it helps readability and consistency. Additionally, any leading and trailing whitespace is removed ("stripped") before processing. Here's a simple example:

DirectiveName value

Besides specifying directive values inline, one can conveniently use the following syntax to obtain value from external files:

DirectiveName <include_filename

[Note]Note

Note that this syntax can be used anywhere on a line, such as in Variable MYSTUFF <file. You can use this to achieve the best performance with Variables.

Files included from interchange.cfg are relative to ICROOT. Files included from catalog.cfg are relative to specific catalog's CATROOT.

So-called "here document" syntax is supported as well. You can use it to spread directive values over several lines, with the usual Perl <<MARKER syntax (but unlike Perl, Interchange syntax uses no semicolon to terminate the marker). The closing marker must be the only thing on the line. No leading or trailing characters are allowed, not even whitespace. Here is a hypothetical directive using a here document:

DirectiveName <<EOD
    setting1 setting2
    setting3
EOD

The above is equivalent to:

DirectiveName setting1 setting2 setting3

Other configuration files can also be included from the current one. For example, common settings can be defined in a single file:

include common.cfg

Or all files loaded from a directory:

include usertag/*

Parsing rules

Conditional blocks

The familiar ifdef/endif and ifndef/endif pairs can be used to affect configuration processing:

Variable ORDERS_TO email_address

ifdef ORDERS_TO
  ParseVariables Yes
  MailOrderTo __ORDERS_TO__
  ParseVariables No
endif

ifdef ORDERS_TO =~ /\@foo\.com$/
  # Send all orders at foo.com to one place now
  # Set ORDERS_TO to stop default setting
  Variable  ORDERS_TO  1
  MailOrderTo   orders@foo.com
  endif

ifdef ORDERS_TO eq 'nobody@nowhere.com'
  # Better change to something else, set ORDERS_TO to stop default
  Variable  ORDERS_TO  1
  MailOrderTo   someone@somewhere.com
endif

ifndef ORDERS_TO
  #Needs to go somewhere....
  MailOrderTo  webmaster@mydomain.local
endif

Apache-like statement blocks

It is possible to define configuration directives for the duration of the block, using the <DIRECTIVE VALUE> ... </DIRECTIVE> notation:

Variable HELLO Hello World!

ParseVariables No

Message Our greeting is: __HELLO__
# Will print: Our greeting is: __HELLO__

<ParseVariables Yes>

Message Our greeting is the shiny __HELLO__
# Will print: Our greeting is the shiny Hello World!

</ParseVariables>

Message Our greeting is back to: __HELLO__
# Will print: Our greeting is back to: __HELLO__

Variables and expansion

Interchange, of course, offers a way to define variables. Variables defined in your interchange.cfg or catalog.cfg can be referenced from both configuration files themselves and the usual Interchange pages later, when the catalog is running.

Variables are defined using the Variable directive (reading its short reference now would be a good idea). The usual way to expand a variable to it's value is to use the __VARIABLE_NAME__ notation. This notation, however, is by default not enabled in RHS ("Right-Hand Side") values in configuration files. To enable it, use the ParseVariables directive which immediately affects the way Interchange parses variables in config files. Here's an example to clarify what we're talking about:

# Let's define two variables
Variable   SERVER_NAME  myhost.mydomain.local
Variable   CGI_URL      /cgi-bin/ic/tutorial

# Let's make VendURL directive be a combination of __SERVER_NAME__ and __CGI_URL__
VendURL  http://__SERVER_NAME____CGI_URL__

# To your surprise, after the above, VendURL would literally contain
# "http://__SERVER_NAME____CGI_URL__". This is not what we want, so
# we need to enable ParseVariables to achieve the desired effect:
ParseVariables Yes
VendURL  http://__SERVER_NAME____CGI_URL__
ParseVariables No

# VendURL now contains "http://myhost.mydomain.local/cgi-bin/ic/tutorial"

Subroutine watches

It may come to you as a surprise, to learn that any configuration directive can be "tied" to a Perl subroutine (if the Tie::Watch Perl module is installed). This allows for a kind of triggers, watch points, or numerous other interesting applications.

Similar to "here documents" ("<<"), subroutine watches are defined using the "<&" notation. Consider the following example:

MailOrderTo orders@myhost.mydomain.local
MailOrderTo <&EOF
sub {
	my($self, $default) = @_;
	if($Values->{special_handling}) {
		return 'vip@myhost.mydomain.local';
	}
	else {
		return $default;
	}
}
EOF

When Interchange tries to retrieve the value of the MailOrderTo configuration directive (usually to e-mail out an order), our subroutine watch is called. In turn, it returns a special value (a separate e-mail address) for customers having value "special_handling" defined in their session. For the rest, it simply returns the default value.

Now that you've grasped the basics, there's more to the story. From the above example, you see our watch subroutine was called in style of &{$subref}(SELF, PREVIOUS_VALUE). "SELF", meaning what it usually means in Perl code, is a reference to the appropriate Tie::Watch object. "PREVIOUS_VALUE" is simply the previously set value for a directive (usually its default). Those are the standard two arguments we receive in a subroutine watch if the configuration directive was of type SCALAR (defined to accept one string or text value).

[Note]Note

Subroutine watches must be defined after the configuration directives have been set to their values. Setting values after subroutine watches will simply destroy them (the watches) and have unpredictable effects.

If the configuration directive being watched was a list (type ARRAY), the subroutine would be called in pattern &{$subref}(SELF, INDEX, PREVIOUS_VALUE). ("INDEX" would be an array index of the item accessed). Setting watch points on arrays that you don't control completely is not recommended. (Namely, most Interchange subroutines call arrays in their list context, and no access method is provided for that).

Finally, if the configuration directive watched was a hash (type HASH), the subroutine would be called in pattern &{$subref}(SELF, KEY, PREVIOUS_VALUE). ("KEY" would be a name of the hash value accessed). In the following example, we tie the Variable configuration directive. This is not recommended for performance reasons — Variable directive is called very often and should not bear any extra overhead). But it illustrates the power of this operation:

Variable TESTIT Unwatch worked.

Variable <&EOV
sub {
  my ($self, $key, $orig) = @_;

  if($key eq 'TESTIT') {
    # only the first time
    if($Scratch->{$key}++) {
      $self->Unwatch();
      return $orig->{TESTIT};
    }
    else {
      return "Tie::Watch works! -- name=$Values->{fname}";
    }
  }
  else {
    return $orig->{$key};
  }
}
EOV

The first time __TESTIT__ is called for an individual user, it would return the string "Tie::Watch works! -- name=NAME" along with their first name (if they provided one at some point). On a second access (again, for an individual user), the watch would be dynamically dropped and the default value of the variable TESTIT returned. All other variables would operate as usual.

cookie

Cookies are typically short key/value parts supported by the HTTP protocol. Their importance is in the fact that the server can send them to clients, and read and modify their value. In addition, cookies have their expiry time, which can be set, also by the server, to any intended value. Whether Interchange will try to land session cookies in clients' browsers is determined by the Cookies directive, and their default expiry time is set by SaveExpire.

Clients can control whether they reject or accept cookies from all or some sites, and can enforce their expiry time.

Web page requests arriving from users are "anonymous" and basically unrelated to each other even if they are coming from the same user. This is because the HTTP protocol is "stateless" and server can't map requests to specific clients based on just the IP addresses it sees. Therefore, cookies are a crucial mechanism for preserving state information in programs with web-based interfaces. By reading the session ID value (stored in a cookie on client's computer), the server can now recognize associate users with their ongoing, active sessions.

[Note]Interchange and its non-dependence on client cookies

Many web-based solutions require that the clients accept storage and retrieval of cookies. When cookies are not enabled on client side, the usage of the site is limited, or clients are even denied access completely.

Interchange, on the other side, does not require client support for cookies. If the storage of cookies is denied or unsupported by the client, Interchange appends session information in generated URLs and uses them to continue keeping track of user sessions. (An example session ID "embedded" in an URL looks like ?id=YjiSdrek).

Session IDs embedded in URLs should theoretically be equivalent to cookies, and they almost are. The drawback is namely the fact that once you visit a non-Interchange page, you lose the id= argument from the URL (because Interchange isn't there to insert it appropriately). The end result is the users will lose their session information when they return back to Interchange pages, unless they copied the session ID information and put it back in the URL manually (which is a bit too much to expect from random visitors). In practice, this isn't a problem when you're not sending users out of Interchange space, and when you build the site with this fact in mind.

When a new client accesses its first page from the Interchange catalog, Interchange sends it both the requested page and a cookie. At that point, Interchange can't know whether the client accepted the cookie or not — it has to wait for the client to initiate the second page request. (Historically, many application servers always bounced the first request to provoke the second access from the client and to check for cookie support. Interchange does not do it.)

If the user sends a cookie back to Interchange (which, as you see, can happen no sooner than on second request), Interchange knows the client is cookie-capable and there's no need to embed session ID in URLs.

One possibly confusing thing is that, by default, Interchange always appends session ID information to the URLs it generates — even if clients have no cookie-handing problems. If the scratch variable mv_no_session_id is set in the user's session, the session ID will not be appended to the URL. Furthermore, on a somewhat related note, if the scratch value mv_no_count is set, then the page count (mv_pc=random) will not be appended either. "PC" is a page counter that serves to prevent client browsers from caching pages.

crypt

> In any case, you can create the encrypted pwds with:
>
> 1) makepasswd --crypt
>   makepasswd --crypt-md5
>
> 2) or help yourself with htpasswd/htpasswd2 and htdigest/htdigest2
>   that come with apache (htpasswd for crypt(), htdigest for MD5):
>
>   htpasswd2 -c /tmp/temp test
>   htdigest2 -c /tmp/temp test test

It's done with simple Unix crypt by default.

To have Apache's htpasswd output to stdout instead of a file, you can do:

htpasswd -n test

Jon

DOCROOT

DSN

Data Source Name or a DSN is an application's data source identifier. It provides all parameters for the connection to a database.

In its simplest form, it has the format of protocol: subprotocol: host: port: database

It's also possible to explicitly name the parameters, like this: protocol: subprotocol: database=DBNAME;port=PORT

DocumentRoot

database

In general, databases contain information, usually in tabular format, where columns define the names and types of contained data, and rows represent entries — database records.

Interchange is primarily using databases to just retrieve values from specific tables, and does not use any higher-level functions of RDBM databases (such as views, triggers, or stored procedures in PostgreSQL). Such things can, however, be implemented in the database independently of Interchange, as Interchange will properly pass any warning or error messages back and forth.

We should say right away that Interchange is completely database-independent. The choice of actual database types that can work with Interchange is large, and Interchange can use some database-like methods automatically when you're not explicitly interested in paying attention to databases working behind the scenes.

Common features are transparently available everywhere (with absolutely no code hacks or special cases required), regardless of the underlying database type used. In addition, almost no field names are hard-coded, allowing for unlimited flexibility.

Keep in mind that the terms database and database table actually mean the same thing in Interchange parlance - a database table.

Interchange works with GNU DBM, DB_File, SQL, LDAP and in-memory types of databases. Regardless of type or other characteristics, each database must be registered on a catalog level before it's ready to be used, and this is achieved using the Database configuration directive. It's useful to remember at this point that multiple catalogs can share the same database.

Three parameters need to be present in a basic Database definition: an arbitrary database name, text source file with initial content, and the type of the database.

Text Source Files

Text source files are not databases themselves, of course (for performance and other reasons); they are only used to provide initial data for the corresponding database tables.

By default, all database source files are kept in the products/ subdirectory of your CATROOT. The ProductDir directive controls the exact location.

The ASCII files can contain carriage return (^M) characters even in data fields, but must have a newline character (^N) at the end of line to properly separate records. Mac users uploading files must use ascii mode, not binary mode.

Interchange's default ASCII delimiter is TAB. Keep in mind that the items must be separated by a single delimiter (that is, by a single TAB only). Due to the nature of TABs, TAB-delimited files look messy and unaligned when viewed in a text editor. Do not try to fix these; better use the te utility that comes as part of the Interchange distribution to edit such files more conveniently.

Interchange can manage an unlimited number of arbitrary database tables and database table types. Several flexible delimiter schemes are available "out of the box":
  • TAB-delimited file (Type 1, the default): Fields are separated by TAB characters. No whitespace is allowable at the beginning of the line.

    code	description	price image
    SH543	Men's fine cotton shirt	14.95	shirts.jpg
    

    (You might notice that the field names and values above are not properly aligned. This is the nature of tab delimited files.)

    Using the default TAB delimiter is recommended if you plan on searching the ASCII source file of the database.

  • LINE (Type 2): Fields are specified each on its own line, separated by the newline (\n) character. One blank line separates a record from another record.

    code
    description
    price
    image
    
    SH543
    Men's fine cotton shirt
    14.95
    shirts.jpg
    

  • %% (Type 3): Fields are separated by the literal combination of "\n%%\n", while the records are separated by "\n%%%\n". Users fond of the Unix "fortune" program may find this format familiar.

    code
    %%
    description
    %%
    price
    %%
    image
    %%%
    SH543
    %%
    Men's fine cotton shirt
    %%
    14.95
    %%
    shirts.jpg
    

  • CSV-delimited file (Type 4): Fields are enclosed in quotes and separated by commas. Again, no whitespace should be at the beginning of the line.

    "code","description","price","image"
    "SH543","Men's fine cotton shirt","14.95","shirts.jpg"
    

    CSV-delimiter schemes might cause problems with ASCII text searching routines.

  • PIPE-delimited file (Type 5): Fields are separated by the pipe ("|") characters which resemble vertical lines. No whitespace is allowable at the beginning of the line.

    code|description|price|image
    SH543|Men's fine cotton shirt|14.95|shirts.jpg
    

    PIPE-delimited files perform fairly well with ASCII text searching routines.

  • TAB-delimited file (Type 6):

  • <reserved> (Type 7):

  • SQL (Type 8):

  • LDAP (Type 9):

[Note]Note

Field names are usually case-sensitive (in fact, that depends on the underlying database type). Always be consistent when naming or referencing fields and you'll avoid the trouble. All lower or all upper case names are recommended.

If a database is specified to be one of the first six types, then the database will automatically be converted to a more efficient internal structure. Those include DB_FILE, GDBM, or MEMORY. The order of preference and the selection is:

SQL Databases

As hinted above, you do not need to use an external SQL database. If you only have a small data set, you could use Interchange's internal databases. This is a tremendous gain for small and quick setups, or ad-hoc Interchange evaluation. However, some functions (order management, for example) will be slower and not as robust without an SQL database. SQL is strongly recommended for at least the state, country, orderline, transactions and userdb tables. Any other tables that will have programmed updates, such as inventory, are also best placed in SQL.

[Note]Database performance

Do not, however, try to optimize too soon and for no measurable difference. Do not fall in the jaws of premature optimization, your worst enemy.

Generally, you should make an additional effort of configuring and using SQL databases to achieve Interchange's full potential. Using SQL also makes your data sets easily available for integration with other applications.

In any case, database import and conversion routines are already available in Interchange and you can use them at any point.

Speaking of the source files' behavior, if a file named table.sql is present in the same directory as table.txt, then database table will never be imported from the ASCII text source file. If there is no table.sql, the DBI/SQL import will happen once, at Interchange startup or catalog reconfiguration time (and the table.sql file will be created); Interchange will connect to the SQL database using the specified DSN (DSN is a standard DBI parameter meaning "Database Source Name"). The table will be dropped (if it already exists in the database) using a line similar to DROP TABLE table. This will occur without warning, but NoImport can be used to prevent it or otherwise change the default behavior. The table will then be created again and populated with text source file data.

If there are any COLUMN_DEF specifications present in interchange.cfg, catalog.cfg or products/table.sql, they will be used to create SQL table specification (which is recommended for clean and correct database layout). If there aren't any, however, then the key (first field in the text file, by default) will be created with the type char(16), and all other fields will be created as char(128). This is very unfortunate, but the best Interchange can do without your help. Table creation statements will be written to the error.log along with, of course, any errors. From our experience, the most common mistake at this point is choosing column names that sound perfectly reasonable, but also happen to be reserved keywords in MySQL. (The error messages appear to be misleading here, so you better take a look at the list of reserved MySQL keywords before losing patience with the problem).

Once the database (database table actually, remember?) is created, the text source file will be imported into it. For this step to succeed, data typing must be user-configured. In other words, if say, word "none" is placed in a field while the field in question is defined to be of numeric type, database import will not succeed; consequently, the problematic catalog won't configure successfully (it will be skipped) and it won't be available when Interchange starts up.

For a complete discussion, please see the Database configuration directive.

File-based Databases

By file-based databases we primarily assume GNU DBM and DB_File. We also call those database types "internal", since in the absence of say, an SQL definition, all inferior formats (such as text source files) are automatically converted to some kind of a file-based database.

Those database types usually work in a way that, on every client access, the appropriate database text source file is checked for being newer than the actual DB file itself. When it happens that it is, the database table is re-imported from the text source file on the fly, and the routine then proceeds as usual.

[Note]Database updates

It is important to note that, when using Interchange internal database methods, all changes in the text source files cause the databases to be re-created. This can have unwanted effects if the database was modified from within Interchange and the contents have not been written back to the text source files. Another common problem are larger data sets that take noticeable time to get imported to (or exported from) the internal database.

The exact behavior can be controlled via the NoImport config directive, but by default, changes in text files will trigger a complete rewrite of DBM or DB_File databases.

To check if you have GNU DBM and GDBM Perl support available, run perl -le'require GDBM_File and print "I have GDBM."'. To check if you have Berkeley DB and DBM Perl support available, run perl -le'require DB_File and print "I have Berkeley DB."'. Sometimes you want to use Berkeley DB even if GNU DBM is installed and would naturally take precedence; in such cases, set the MINIVEND_DBFILE environment variable to a true value (setenv MINIVEND_DBFILE 1 in csh, MINIVEND_DBFILE=1 ; export MINIVEND_DBFILE in sh, b(a)sh or ksh). It is also possible to use Berkeley DB for just specific databases.

For a complete discussion, please see the Database configuration directive.

Memory Databases

Memory Interchange databases use Perl hashes to store the data directly in memory. Every time the Interchange server is restarted, it will re-import all in-memory databases for every catalog.

Memory databases are used by default only if no database type is explicitly specified, and there is no DB_File or GNU DBM found on the system. Otherwise they can be used for small but high-traffic tables. Keep in mind, however, that since their contents are not saved back to the text files, you'll want to either take care of the data export yourself, or keep the tables stuffed with read-only data.

if you want to force memory databases despite of GDBM_File or DB_File being present, set the MINIVEND_NODBM environment variable to a true value (look previous chapter for hints on setting it). It is also possible to use memory type for just specific databases.

Memory databases import will be performed once at every Interchange startup or catalog reconfiguration time.

For a complete discussion, please see the Database configuration directive.

Interchange Database Design

We are trying not to impose any database structure that would require our own tools to maintain the data. We always want to keep it such that Interchange data can be maintained via a spreadsheet processor or foreign database tools.

Interchange Database Conventions

This section describes naming and file usage conventions used with Interchange. This is very important for both understanding Interchange and developing your own custom solutions which build upon officially recommended practices.

Term definitions:

  • key or code

    The words reference the database key. In Interchange, the key is usually the product code or SKU, which is the product part number. Otherwise, key values may be used to generate relationships to other database tables.

    It is recommended that the key be the first column of the database text source file, since Interchange's import, export, and search facilities rely on this practice.

  • field or column

    The vertical row of a database. One of the columns is always the key and it is usually the first one, as explained above.

  • table or database

    A table in the database. Because Interchange has evolved from a single-table database to an access method for an unlimited number of tables (and databases, for that matter), a table will occasionally be referred to as a database. The only time the term database refers to something different is when describing the concept as it relates to SQL, where a database contains a series of tables. While Interchange cannot create SQL databases, it can drop and create tables within databases if given the proper permissions.

Interchange uses one mandatory database, which is referred to as the products database. In the supplied demo catalog (and in the most of real-world solutions as well), the primary database is directly called products and the ASCII source is kept in the products/products.txt file. This is also the default file for searching contents with the search engine, such as Glimpse, HTDig or Swish.

[Note]Note

Interchange also has two optional databases that are specified in special, fixed formats:

  • shipping.asc database contains shipping options that are accessed if the CustomShipping directive is in use. This is a fixed-format database, and must be created as specified. For more information, see shipping glossary entry and the shipping tag.

  • salestax.asc database contains sales tax information if the salestax tag is to be used. A default is supplied. Caution, these things change and need periodic updating! See tax glossary entry for more information.

The two above tables cannot be stored in any user-specified format.

debug

No debugging options are enabled by default in Interchange. See DebugFile for a quick introduction to enabling debug messages.

Before DebugHost can be used to restrict diagnostics to specfic set of hosts, the debug mode itself has to be enabled using DebugFile.

dereference

Dereferencing is strictly a computer-programming issue, but we will try to explain it in very brief and comprehensible terms, so that you understand the idea of dereferencing and its practical effect when data structures are copied.

Let's say we want to compose a list of a few automobiles. Each entry in the list will contain the fields model, year and mileage.

Theoretically speaking, to solve this real-world problem with the help of a computer, we would create a template (containing the three fields), and produce one instance of the template for each car we add to our list. (How this list is created, how the elements are added and how they relate to each other is irrelevant here).

One imaginary list with three instances could be visually represented in the following way:

             Model     Year  Mileage
  list[0] { 'Fiat',    1996, 177940 }
  list[1] { 'Citroen', 2001, 66000  }
  list[2] { 'Citroen', 2002, 23000  }

There is only one copy of this list in computer memory, and we read or modify the elements by obtaining references (or, pointers) to appropriate entries.

If we take list above to contain the list of references to the entries, we can use list[0].Model to retrieve the value "Fiat", or list[2].Year to retrieve "2002". For both of those fields, a reference was first dereferenced (or, followed) to reach the actual data fields.

When list elements need to be copied to another location (usually as part of some bigger plan which, again, we are not interested in), they can be copied by value (with dereferencing) or by reference (obviously, without dereferencing). With copy by value, you would end up with 2 references and 2 different lists (initially they would be the same but afterwards you could modify each with no connection to the other). In case of copy by reference, you would again have 2 references, but both pointing to the same list. Modifying data through any of the two references would have impact on both.

So, when a data structure (or its element) is said to be copied without dereferencing, then in case it was a reference, it is still copied in itself, but all copies point to the same location. In other words, the data is not duplicated, only the access points are.

discount

Product discounts in Interchange can be set at any time. The discounts apply only to the customer receiving them, so you can set discounts based on membership in a club or other arbitrary means.

Discounts are defined using the discount tag, and are of the following types:

  • Discount on a specific item - a discount for one particular item. Key to use with the discount tag is the product's SKU

  • Discount on all items - a discount applying to all items. Key to use with the discount tag is ALL_ITEMS

  • Discount on a particular item at particular time - a discount for an individual line item, applied if the mv_discount attribute is set (usually with embedded Perl)

  • Order discount - a discount applied not to individual products, but to the total order amount. Key to use with the discount tag is ENTIRE_ORDER

Discounts within the discount tag are specified using a formula. The formula is scanned for the $q and $s variables which are substituted for the item quantity and subtotal respectively. The variable $s is saved between iterations, so that the discounts can be cumulative.

In case of individual item discounts, the formula must be constructed to handle all instances of a particular SKU found in the user's basket. There are many ways how same SKU might occur multiple times in the user's basket (for example, with SeparateItems enabled) — the same formula will be invoked on every occurrence and it should always give out the correct individual subtotal.

In case of an entire order discount, the formula is usually simpler and defines a flat discount amount or percentage.

Discounts are applied to the effective price of the product, that is — the price obtained after applying price adjustments.

For examples, see the discount reference page.

In April 2005, Interchange added support for "discount spaces" (using CGI variable mv_discount_space), in a manner akin to values space (mv_values_space) or named shopping cart (mv_cartname). See DiscountSpacesOn and DiscountSpaceVar for usage examples.

dispatch

This entry describes how Interchange processes a request after assiging it to a catalog.

  1. Running macro(s) defined by Preload directive.

  2. Checking authorization.

  3. Session, cookie and robot handling.

  4. Redirect GET requests with affiliate code when BounceReferrals directive is enabled. Effectively terminates processing of the request.

  5. ...

environment

epoch

Unix Epoch resource at Wikipedia.

escape

expire

User sessions in Interchange are usually kept as files in the session/ directory (or inside a DBM database) for each catalog. Since session data is not deleted after sessions end (or timeout), periodic expiring needs to be set up to keep the session database or session files from growing too large, wasting disk space and slowing down directory lookups.

There's no worry that expiring will do any harm, because all our scripts only clean up unused sessions. Active users will not notice any change.

The simplest way to expire catalog's session files is to run expire -c CATALOG_NAME.

For convenience, there is also expireall script which reads all catalog entries in interchange.cfg and runs expire on them.

The expire script accepts a -r option which, when DBM sessions are used, tells the script to reorganize database files and recover lost disk space.

On a UNIX server, it's most useful to run expireall from crontab. As the Interchange user, run crontab -e to edit crontab data, and enter something like:

# once a day at 4:40 am
40 4 * * *    /PATH/TO/perl /PATH/TO/INTERCHANGE/bin/expireall -r

[Note]Note

If a session saved search paging files in ScratchDir, they would not be deleted (see below for a solution).

When file-based sessions are used (no DBM), then you can use a custom script like this:

#!perl
# expire_sessions.pl -- delete files 2 days old or older
# invoke as: /PATH/TO/perl expire_sessions.pl /PATH/TO/CATALOG/session/ ...

my @files;
my $dir;
foreach $dir (@ARGV) {
	 # just push files on the list
	 if (-f $dir) { push @files, $_; next; }

	 next unless -d $dir;

	 # get all the file names in the directory
	 opendir DIR, $dir or die "opendir $dir: $!\n";
	 push @files, ( map { "$dir/$_" } grep(! /^\.\.?$/, readdir DIR));
}

for (@files) {
	 unless (-f $_) {
			 warn "skipping $_, not a file.\n";
			 next;
	 }
	 next unless -M $_ >= 2;
	 unlink $_ or die "unlink $_: $!\n";
}

This script can be adjusted as necessary. Refinements might include reading the file to "eval" the session reference and expire only customers who are not registered members.

If your files get chown-ed to root every day, then you probably used root's instead of Interchange user's crontab file. Either move the crontab to the Interchange user, or use su to swith users from the root account:

44 4 * * * su -c "/PATH/TO/INTERCHANGE/bin/expireall -r" IC_USERNAME

The above does not, however, clean temporary files from the ScratchDir directory. We don't often use the expire scripts any more. We just use a small standalone script clean_session_tmp:

#!/bin/sh

for DIR in $*; do
  for i in session tmp; do
    if test -d "$DIR/$i"; then
      find $DIR/$i -type f -mmin +480 | xargs --no-run-if-empty rm
      find $DIR/$i -depth -type d -empty -mtime +2 | xargs --no-run-if-empty rmdir
    else
      echo "$0: $DIR/$i doesn't exist.";
    fi
  done
done

using a cron entry similar to:

44 0,4,8,12,16,20 * * * DIR/bin/clean_session_tmp /path/to/catdir1 /path/to/catdir2

external

The set of configuration directives prefixed "External" allows export of Interchange variables for use by other programs and languages, such as PHP, Ruby or Python.

The three directives are External, ExternalFile and ExternalExport.

The current functionality is just a proof of concept and is not for serious use. The interface needs serious work.

A working example for accessing data from PHP is provided in the External reference page.

false

In general programming terms, a false value is one that is either 0 (zero) or "" (an empty string).

Since the Perl programming language has a notion of undefined values, they too are considered false.

feature

With Interchange 5.3.0, Interchange supports so-called "features". The whole purpose of the new "Feature" facility is to allow easy installation of new capabilities to Interchange.

Interchange already has the convention of "extensions" which allow you to put together features to add to Interchange. But the installation is manual, and requires good docs to make it easily installable for end-users. Also, many features require access to the global configuration. There's also another problem at sight, namely that of feature creep, since everything was just being added to the "standard" catalog.

The basic mechanism is simple: Inside "feature" modules, there are three special kinds of files, called by extensions .global, .init and .uninstall. (In the included quickpoll feature, these are named quickpoll.global and quickpoll.init).

If a file has the extension .global, it is added to the global configuration. The included quickpoll feature, for example, adds the quickpoll ActionMap, and two usertags: poll-answer and ascii-graph.

If a file has the extension .init, it is run once — the first time the target catalog is accessed. Again, in the quickpoll example, it is used to add mv_metadata entries and a couple of sample polls.

All other files in the directory are catalog configuration (quickpoll.catalog.cfg for a concrete example). It could have also been broken up into say, files quickpoll.sql and quickpoll_answer.sql.

All subdirectories contain files which are copied to the catalog directory with the same relative path. In this case, ICROOT/features/quickpoll/templates/components/quickpoll would go to CATROOT/templates/components/quickpoll.

The .init file, when run, sends its output to ConfDir/init/FEATURE/FEATURE.init (and that would be etc/init/quickpoll/quickpoll.init for the concrete example.) Once it is run, the existence of the file prevents it being run again.

Uninstall files, those with the .uninstall extension, are ITL files that can perform any uninstall functions, and they run with temporary AllowGlobal access to allow dropping of tables, unlinking of files, etc.

Automated uninstall features include removing any files installed as a part of the feature -- providing they have not changed. If the file was edited and is not identical to the originally installed file, then it is left there and a warning issued. Uninstall creates file ConfDir/init/FEATURE/uninstall to note the uninstall, and which prevents the Init process from happening again (assuming Interchange has not been restarted since the feature installation).

The uninstall routine is called with the uninstall_feature tag.

[Caution]Caution

The catalog user must remove the Feature directive from their catalog.cfg before running uninstall_feature, otherwise the Feature will be re-installed the next time Interchange is restarted!

Also, there is a short window where a SQL table, if dropped as a part of the uninstall procedure, could be re-instantiated based on the existence of the configuration in memory. It is recommended that if uninstall_feature is called, a reconfig tag is called shortly thereafter to reduce this possibility to a minimum.

filter

Interchange filters are usually small routines that perform various (arbitrary) transformations of user input.

There are filters that trim the text to a specified maximum length, substitute characters in strings, make user input display- or storage-safe, or even serve as value equivalents.

Using existing filters and creating new ones is very simple.

flypage

form action

Interchange "form actions" are basically Perl subroutine definitions that you can execute upon form submission. Which form action to trigger is specified by the mv_action form variable.

Successfully executed form action should return a true value, upon which Interchange would display the page specified in mv_nextpage. If the form action returns false, Interchange will not display any page.

Using FormAction, you can both define new and override existing form actions.

form processor

GET

"GET" resource at Yukka Korpela's website and faqs.org.

gate

In Interchange parlance, "gating" is the process of controlling access to certain pages. See Q:  HOW-TO entry for further information and examples.

[Note]Note

In Interchange versions prior to 5.7.0, .access file found directly in PageDir (i.e. pages/) is mistakenly ignored, but does work for subdirectories.

gateway

Gateway Configuration

The first step is to fulfil the prerequisites of the payment module (listed in the individual module documentation below) and enable the module in the global configuration file with the Require directive:

require Vend::Payment::NetBilling

If we are using only one gateway in a catalog, setting up MV_PAYMENT variables is sufficient.

VariablePurposeNotes
MV_PAYMENT_MODEgateway mode name 
MV_PAYMENT_HOSTgateway host nameoptional (predefined in module)
MV_PAYMENT_IDmerchant identifier 
MV_PAYMENT_SECRETsecret part of credentialspassword or certificate
MV_PAYMENT_REFERERreferring URL 
MV_PAYMENT_CURRENCYcurrencythree-letter currency code according to ISO 4217
MV_PAYMENT_TESTtest mode 

An example configuration looks like:

Variable MV_PAYMENT_MODE signio
Variable MV_PAYMENT_PARTNER verisign
Variable MV_PAYMENT_ID nevairbe
Variable MV_PAYMENT_SECRET foobar

Multiple Gateways

With the Route directive it is possible to specify payment gateways for special purposes. The payment route should set all relevant payment parameters for the gateway, otherwise the settings from the MV_PAYMENT_* may leak into the route.

Route signio partner verisign
Route signio id nevairbe
Route signio secret foobar

Transaction Types

The following payment gateway transactions are known by Interchange:

TransactionDescription
avsAddress verification (AVS)
authPayment authorization (Charge)
returnCredit
reverseReverse former transaction
saleCharge and capture
settleCapture of an authorized charge
voidCancel or refund payment
abortAbort pending capture.

Payment Modules

Modules for the following payment gateways are included in the Interchange source code:

ModuleNameModeDescription
AuthorizeNetAuthorizeNetauthorizenet 
BoABank of Americaboa 
BusinessOnlinePaymentBusiness::OnlinePaymentonlinepaymentwrapper for Business::OnlinePayment
CybercashCybercashcybercash 
ECHOElectronic Clearing House, Inc.echo 
EFSNetConcord EFSNetecho 
EzicEziCezic 
GetitcardGetitcardgetitcardPrepaid cards from Getitcard.
ICSCybersource ICSICS 
iTransactiTransactitransact 
LinkpointLinkPointlinkpoint 
MCVEMainstreet Credit Verification Enginemcve 
NetBillingNetBillingnetbilling 
PRIPayment Resources InternationalPRI 
PSiGatePSiGatepsigate 
SageSage Paymentsage 
SignioPayflow Prosignio 
SkipjackSkipjackskipjack 
TCLinkTrustCommercetrustcommerce 
TestPaymentPayment Testtestpaymentfor test purposes

AuthorizeNet

The AuthorizeNet module implements the ADC Direct Response method for AuthorizeNet version 3.

Prerequisites

Either Net::SSLeay or LWP::UserAgent and Crypt::SSLeay from CPAN.

Parameters

Required parameters are id and secret.

id

The AuthorizeNet account ID, received from AuthorizeNet after sign up.

secret

The AuthorizeNet account password, received from AuthorizeNet after sign up.

ECHO

Prerequisites

OpenECHO module from http://www.openecho.com/.

Getitcard

The Getitcard payment module is used for purchases with prepaid cards issued by GetitcardŽ (http://www.getitcard.com/).

Prerequisites

Either Net::SSLeay or LWP::UserAgent and Crypt::SSLeay from CPAN.

Parameters

Required parameter is id.

LinkPoint

Parameters

Required parameters are id and keyfile.

host

The domain name of the LinkPoint Secure Payment Gateway (LSPG). Default is secure.linkpt.net for production and staging.linkpt.net for testing.

id

Store number assigned to your merchant account.

keyfile

File name of the merchant security certificate. This file should contain the RSA private key and the certificate, otherwise you get an error like "Unable to open/parse client certificate file."

transaction

The type of transaction to be run. Valid values are:

InterchangeLinkpoint
authpreauth
salesale

Default is sale.

Netbilling

The Netbilling module implements the Netbilling Direct Mode 2.1.

Prerequisites

Parameters

host

The domain name of the NetBilling secure server. Defaults to secure.netbilling.com.

id

This is your account and sitetag separated by a colon (ACCOUNT:SITETAG). ACCOUNT is the number of your Netbilling merchant or agent account, as a 12-character string. Required for ALL transactions. SITETAG is the site tag of your website configured in the Netbilling system. Required for membership transactions, optional for others.

Sage

Parameters

Required parameters are id and secret.

Signio

Parameters

Required parameters are partner, id, and secret.

TCLink

Prerequisites

Net::TCLink module from http://www.trustcommerce.com/tclink.html or CPAN.

TestPayment

This module can be used for test purposes.

Card Numbers

If the card number is 4111111111111111 then the transaction will be approved.

If the card number is 4111111111111129 then the transaction will be declined.

Any other card number will raise an error and the transaction will be declined.

HTML

HTML resource at W3C.

hash

hello world

Hello, World! resource at Wikipedia.

ICROOT

ISBN

See ISBN Wikipedia entry.

ITL

Interchange functions are accessed via the Interchange Tag Language (ITL). The pages in a catalog may be mostly HTML, but they will use ITL tags to provide dynamic content and access Interchange functions in general. ITL is a superset of MML, or Minivend Markup Language.

ITL tags perform all kinds of operations. There's more than 200 standard predefined tags, and the UserTag facility allows you to create your own custom tags, indistinguishable from the ones "built-in". To get started with custom tags, see usertag glossary entry.

Basics

ITL tags are similar to HTML, in that they accept attributes and that there are both standalone (non-container) and container tags.

A standalone tag has no ending element, such as value:

[value name]

The above example will insert user's name.

A container tag has both beginning and ending elements, such as tag if:

[if value name]
  You have provided us with your name. It is [value name].
[/if]

In the above example, you see that container tags are in general useful only when content is provided in their body (the place between the opening and corresponding ending tag).

There must be no whitespace between the left bracket ([) and the tag name; [forum] is valid, [ forum] is not!

Standard Syntax

We've covered the most basic syntax above. If you need to pass attributes to the tag, you do that in the opening section. If the tag is a container, then body text can additionally be specified between the opening and closing marker:

[tagname parameters_or_attributes]
  Body Text
[/tagname]

Note that some Interchange macros (drop-in text replacements) may look like tags or end tags. For example, [/page] used to be a macro that expanded to </a>, but page itself was not a container tag and [/page] wasn't its closing element. Likewise, some tags perform special subtags processing on their own, and the subtags are not considered real tags — they only exist in the context of the surrounding tag and usually support only limited options. One such example is tag row with its subtag .

It is important to mention that if a tag or argument name includes an underscore or dash (such as in item-list), then you can always choose between the dash and underscore! The two forms are interchangeable, except that you must be consistent with tag markers; an ending tag must match the opening tag. Both [item-list]...[/item-list] or [item_list]...[/item_list] are fine, but [item-list]...[/item_list] is not.

This is a very convenient feature. It is a common standard to use dashes (-) in ITL, and underscores (_) in Perl code (because in Perl, dashes would be interpreted as minus operators, which is not what you'd expect).

Named and Positional Parameters

There are two styles of supplying attributes to a tag: named and positional.

In the named style, you supply attributes using key=value pairs, just as with HTML:

[value name=variable_name]

The positional-style tag that accomplishes the same thing, but is more compact:

[value variable_name]

Positional syntax, however, requires support by each tag individually. That is, set of arguments that can be specified in positional notation (as well as their position in the set) are fixed and determined in advance. You can see a list of accepted positional arguments (and their implicit order) in each tag's SYNOPSIS reference section; try value for example.

You cannot mix named and positional attributes in the same tag; use either all named, or all positional.

Positional syntax is appropriate for simpler tags and Interchange interprets positional arguments slightly faster, but don't let premature optimization kick in — as Edsger W. Dykstra once said, "Premature optimization is the root of all programming evil".

In most cases, positional attributes (called parameters) work the same as named attributes. However, there are a few situations where you must use named attributes:

  • When you want to specify a parameter that, positionally, comes after a parameter that you want to omit, e.g. omit the first parameter but specify the second. The parser would have no way of knowing which is which, so you must revert to named syntax. This rarely happens, though, because the first positional parameter is usually required for a given tag anyway, and can't be omitted.

  • When there is some ambiguity as to which parameter is which, usually due to whitespace. If the argument value contains any whitespace (even if it is enclosed in quotes!), you must use named syntax.

  • When you need to interpolate the value of an attribute, such as of default= in [value name=time default="[time]"].

  • When you are using looping subtags (Loop tags and subtags are explained below).

HTML Comments

ITL also supports a special notation where you use HTML comment markers with ITL inside, and where the comments are removed by Interchange so that the tag results show up in the generated HTML page.

That means [tagname ...] and <!--[tagname ...]--> are equivalent.

This allows you to make your raw ITL tags appear as comments in HTML browsers, and especially in HTML editors which might not like non-HTML markup at random positions. You might want to use this if your editor has trouble with ITL tags when editing Interchange page templates, or alternatively, if you want to use one .html file both as an Interchange page and as a static page delivered directly by your web server where no Interchange processing is performed.

When you really wish to treat HTML comments as plain comments and not remove them, then you must include a whitespace between the HTML comment delimiters and the ITL square brackets:

<!--[value name]-->       becomes  Bill
<!-- [value name] -->     becomes  <!-- Bill -->

ITL is interpolated in both cases however. To prevent ITL interpolation within HTML comments, you either need to see the <pragma>no_html_comment_embed</pragma> pragma, or enclose the block in a special ITL comment tag instead of in HTML comment:

[comment] [value name] [/comment]

Besides not being interpolated at all, comment blocks do not appear in the final HTML sent to the client either, so you can be completely safe regarding any unintentional code or information leakage.

While <!--[ and [ are completely interchangeable, the Interchange parser does not replace ]--> with ] unless it sees <!--[ at least once somewhere on the page. This is a small parsing speed optimization and shouldn't cause you any trouble as you're supposed to be consistent in your syntax anyway.

Argument Interpolation

When ITL tags are expanded, for example when [value name] yields the actual user's name, we say the tags are interpolated.

If you want to use one tag's output as another tag's input, you must use named syntax and you must double-quote the argument. For example, this WILL NOT work:

[page scan se=[scratch variable_name]]

The above code is in really bad shape. To make it work, we need to do two things: switch to named syntax, and properly quote arguments containing whitespace:

[page href=scan arg="se=[scratch variable_name]"]

Note that the href argument did not need to be quoted; arg did, because it contained a whitespace and it contained a tag intended for interpolation ([scratch variable_name]).

[Note]Note

As you can implicitly see from the above example, attribute values are normally interpolated (in other words, se=[scratch variable_name] was expanded to se=VARIABLE_VALUE before being passed as value to arg=.

However, when the attributes contain a dot (such as name.first= or name.last=), their values are not interpolated by default. Their interpolation is controlled by the <pragma>interpolate_itl_references</pragma> pragma, and more about this whole concept can be learned below in the section called “Attribute Arrays and Hashes”.

Parsing Order

Interchange parse content like pages, components and profiles in a few consecutive passes instead of once from the top to the bottom:

  1. pragmas are set from pragma in the content.

  2. Variables are replaced (__NAME__, @@NAME@@, @_TITLE_@) with their respective values.

  3. Comments enclosed by comment are removed.

  4. Tags are expanded.

Deeper Look at Argument Quoting

The question is — exactly when can you omit the quotes around argument values? First answer is trivial; never omit the quotes and you'll never run into trouble.

The other answer says, "omitting quotes earns you a bonus for style and achieves small parser speed improvement". However, if the value contains a whitespace, you must quote it. To quote values, you can use double quotes ("), single quotes (') and pipes (|) interchangeably. Pipes have the additional functionality of removing leading and trailing whitespace, but generally you can use all types in combination to do three levels of quoting with ITL; for more deeply nested constructs, use direct Perl code.

Additionally, container tags can be closed using "XML-style" syntax. The following two lines are identical in effect:

  [forum top="[item-code]" display_page=forum_display  ][/forum]
  [forum top="[item-code]" display_page=forum_display /]

Note, however, that you can use "/]" only with tags for which you provide no attributes, or the attributes are named and quoted. Positional attributes (parameters) "absorb" everything (including the "/") up to the closing bracket, and therefore using "/]" is not possible.

When using "/]" notation, it is still possible to provide body for the tag by specifying body= attribute.

The above covers a good deal of quoting, but it's still not all there is to it. Loop subtags, which we explain some lines below, are an exception to the rule and do not need quotes (even though they sometimes contain whitespace), because they are parsed earlier in a separate pass, before the general parser takes on.

Here's an example with loop subtags that would work properly even with quotes omitted. (Pay attention to [item-field url] which, at first sight, looks like it is invalid because it contains a space and is not quoted nor named.)

[item-list]
[page [item-field url]]detailed info[/page] on [item-description]
[/item-list]

You might wonder why unquoted tags are allowed. The answer is performance. If you have large lists of tags you can achieve significant speed-ups by using positional attributes. Parsing and disassembling named attributes takes some more CPU cycles.

Accessing Perl from ITL

Perl-related ITL tags

Among ITL tags, perl, calc, calcn and mvasp are used obtain direct access to the Perl language in Interchange pages. In fact, all the ITL tags are only extensions (and convenience features) built on top of Interchange's Perl core, so all ITL tags boil down to Perl code anyway. Using Perl directly could have the benefits of:

  • Increasing page parsing speed (Perl blocks are evaluated directly)

  • Making complicated ITL constructs much easier, or even trivial

  • Enabling direct access to Interchange data structures (and code, and everything)

  • Unifying coding style

Let's say we wanted to display user's real name in a page. Here are three pieces of code, all achieving this effect, but using different approaches to produce the result:

Displaying user's real name using an ITL layer:

[if value name]
  Your name is [value name], in case you forgot.

[else]
  I don't know your name.

[/else]
[/if]

Displaying user's real name by calling ITL tags from Perl:

[calc]
  my $out;
  my $name = $Tag->value('name');

  if ($name) {
    $out = "Your name is $name, in case you forgot.";

  } else {
    $out = "I don't know your name.";

  }

  return $out;                                              
[/calc]                 

Displaying user's real name by accessing Interchange's data structures:

[calc]
  my $out;
  my $name = $Values->{name};

  if ($name) {
    $out = "Your name is $name, in case you forgot.";

  } else {
    $out = "I don't know your name.";

  }

  return $out;                                              
[/calc]                 

Perl-related ITL syntax

ITL also supports a special type of quoting, using backticks (`), where the content is evaluated by Perl and then passed as the usual argument value.

Using this backtick notation, you can conveniently perform quick, in-place Perl evaluation of an argument, or pass complex data structures as attribute values.

Performing quick, in-place argument interpolation:

[cgi name=Magic_Number hide=1 set=`2 + 3 + $CGI->{magic}`]

The magic number is: [cgi Magic_Number]

Passing complex data structures as argument values:

[cgi name=Magic_Structure hide=1 set=`{ key1 => 'val1', key2 => 'val2' }`]

As you see, this notation (and implied functionality) is completely valid and possible. It is then just up to the called tag to handle attribute value appropriately.

[Note]Note

It is only possible to use the backticks notation with named parameters!

Universal Attributes

Universal attributes apply to all tags, although tags might specify their own defaults for the attribute. The code implementing universal attributes is independent of the routines that implement specific tags.

We will explain interpolate and reparse attributes here. It is important to remember that the behavior of the interpolate attribute (unfortunately) differs, based on whether a tag is stand-alone or a container. In addition, the reparse attribute is only used with container tags.

With standalone tags, the interpolate attribute specifies whether the output of the tag will be interpolated.
Output of most of the stand-alone tags is not interpolated by default. Exceptions to this rule would include, for example, the include tag.

With container tags, the interpolate attribute specifies whether the tag body will be interpolated before being passed to the tag.
The reparse attribute specifies whether output will be interpolated.

For an interpolation example with container tags, let's assume the user's name is Kilroy:

[log interpolate=1][value name] was here[/log]

[log interpolate=0][value name] was here[/log]

The first line would log "Kilroy was here" to CATROOT/etc/log, while the second line would log pretty useless "[value name] was here".

For an interpolation example with stand-alone tags, consider the following:

[set name=now interpolate=0][time]%A, %B %d, %Y[/time][/set]

We've set the scratch variable new to not contain the actual date, but the ITL code to calculate and display the date each time it's called. Let's assume today is Monday, January 1, 2001, and we have the following code:

[scratch name=now interpolate=1]

[scratch name=now interpolate=0]

The first line would then produce the expected Monday, January 1, 2001, while the second would leave us with unusable [time]%A, %B %d, %Y[/time].

The example above serves to show interpolate used with a stand-alone tag. This behavior can only be achieved by using reparse attribute if you're having a container instead of the stand-alone tag.



The reparse attribute is only used with container tags, and specifies whether the tag output will be interpolated. This is basically the same as the interpolate attribute for stand-alone tags, but a new name had to be invented because interpolate already performed a different function with container tags, when this functionality was to be added.

Most container tags will have their output re-parsed for more Interchange tags by default. If you wish to prevent this behavior, you must explicitly set the reparse attribute to a false value. Note, however, that you will almost always want the default action. Probably the only ITL tag that doesn't reparse by default is mvasp.

Here's an example. Again, assuming the user's name is Kilroy,

[perl reparse=1]
   my $tagname = 'value';
   return "[$tagname name] was here\n"
[/perl]

[perl reparse=0]
   my $tagname = 'value';
   return "[$tagname name] was here\n"
[/perl]

expands to

Kilroy was here

[value name] was here

In Interchange 5.3.1, the <pragma>no_default_reparse</pragma> pragma was added, which disables the default reparsing of container tags' output. It is therefore advisable to always specify the reparse=1 attribute at places where you want reparsing to really occur.



The third attribute, send , was deprecated long ago and will only be described for historical reference.

Tag-specific Attributes

Each tag may accept additional arguments which vary from tag to tag. For each tag individually, you need to consult the appropriate reference page to learn tag-specific parameters and attributes. Note, however, that the tag arguments do not necessarily have to be announced in the tag definition header; sometimes they're used pretty much ad-hoc, but we are making sure to keep the documentation up to date. We kindly ask you to inform us of any errors or omissions in the documentation.

Attribute Arrays and Hashes

Instead of just plain values, for some tags it would be particularly convenient if they accepted arrays or hashes of argument values, much like you could do if you used Perl directly. Fortunately, ITL supports that too! For an ordinary tag, the syntax is as follows:

attribute.ARRAY_INDEX=value

attribute.HASH_KEY=value

ARRAY_INDEX is an integer array index starting from 0 (this is due to standard programming practice and Perl behavior). Note that you cannot have both an array and a hash attribute of the same name (even though that would be possible in pure Perl).

Here is an example of an array-based attribute:

search.0="se=hammer
          fi=products
          sf=description"

search.1="se=plutonium
          fi=products
          sf=comment"

It is up to each individual tag to handle an array or hash value properly. See the documentation for the specific tag before passing it an attribute array or hash value. The page tag, for example, would treat a search specification array (the structure we've shown above) as a joined search (one of different search types we support).

On the Perl level, before passing attributes to a tag, Interchange parser would convert attributes to an anonymous array reference.

If you were passing the above example directly to a tag named ROUTINE within a perl block or your custom tag, you would actually pass an anonymous array as the value of the attribute:

my $arrayref = [ "se=hammer/fi=products/sf=description",
                 "se=plutonium/fi=products/sf=description", ];

$Tag->ROUTINE( { search => $arrayref } );

Similarly, to use a hash reference in some other occasion:

my $hashref = { name   => "required",
                date   => 'default="%B %d, %Y"', };

$Tag->ROUTINE( { entry => $hashref } );

Pay attention to the fact that with array- or hash-based attributes, their values are not interpolated. It means that, at places where scalar options are interpolated (such as [tag option='[time]'], resulting in time expanding to the current time), reference-based attributes are not (such as [tag my.option='[time]'], which would result in literal "[time]" being passed to the attribute "my.option". Interpolation of reference-based attributes can be enabled by setting the <pragma>interpolate_itl_references</pragma> pragma.

Looping Tags and Sub-tags

Certain tags can only appear in a special context, usually nested within other, parent tags. Those include, for example, the ones interpreted as part of a surrounding loop tags, such as loop, item-list, query or search-region.

Some of those subtags are PREFIX-accessories, PREFIX-alternate, PREFIX-calc, PREFIX-change, PREFIX-code, PREFIX-data, PREFIX-description, PREFIX-discount, PREFIX-discount-subtotal, PREFIX-field, PREFIX-increment, PREFIX-last, PREFIX-line, PREFIX-modifier, PREFIX-next, PREFIX-param, PREFIX-pos, PREFIX-price, PREFIX-quantity, PREFIX-subtotal, if-PREFIX-data, if-PREFIX-field, if-PREFIX-param, if-PREFIX-pos, PREFIX-modifier-name and PREFIX-quantity-name.

In each of the above, PREFIX represents an arbitrary prefix that is used in that looping tag; this is needed to be able to support nesting in arbitrary order and to arbitrary level. All of the subtags are only interpreted within their container tags (they can only appear inside their parent tags' bodies) and they only accept positional parameters. The default prefixes follow:

Parent tagDefault prefixExample sub-tag
loop loop [loop-code]
[loop-field price]
[loop-increment]
item-list item [item-code]
[item-field price]
[item-increment]
search-region item [item-code]
[item-field price]
[item-increment]
query sql [sql-code]
[sql-field price]
[sql-increment]
tree item [item-code]
[item-field price]
[item-increment]

Sub-tag behavior is consistent among the looping tags.

[Important]Important

As we've said already, subtags are parsed before the standard parsing routine takes on; that means before any regular tags within the loop. This has a subtle effect; it can, for example, prevent the usual tags from accepting looping tags' values as attributes. In such cases, look for "subtag" (prefixed) variants of the usual tags.

Speaking of loops and Perl, there are two types of records that Interchange can return with looping lists: ARRAY and HASH.

An array list is the normal output of the query and loop tags, or of a search operation. In those cases, fields specified in mv_return_fields (or those specified in SQL) are returned for each selected row. The two queries below are essentially identical:

[query sql="select foo, bar from products" /]

[loop search="
                ra=yes
                fi=products
                rf=foo,bar
"]

Both will return an array of arrays consisting of the foo and bar columns. The corresponding Perl data structure would look like this (familiarity with the output from Data::Dumper Perl module helps here):

[
    ['foo0', 'bar0'],
    ['foo1', 'bar1'],
    ['foo2', 'bar2'],
    ['fooN', 'barN'],
]

A hash list is the normal output of the item-list tag. It returns the value of all return fields in an array of hashes. A normal return might look like this:

[
    {
        code     => '99-102',
        quantity => 1,
        size     => 'XL',
        color    => 'blue',
        mv_ib    => 'products',
    },
    {
        code     => '00-341',
        quantity => 2,
        size     => undef,
        color    => undef,
        mv_ib    => 'products',
    },

]

However, you can explicitly request hash lists to be returned from queries:

[query sql="select foo, bar from products" type=hashref /]

The data structure returned would then be:

[
    { foo => 'foo0', bar => 'bar0' },
    { foo => 'foo1', bar => 'bar1' },
    { foo => 'foo2', bar => 'bar2' },
    { foo => 'fooN', bar => 'barN' },
]

ic run mode

low, high, rpc, prefork

Single Forks with PreFork

Standard PreFork uses the typical double-fork method in UNIX for generating daemons so as to avoid zombies (or more specifically needing to deal with them). However, forking Interchange daemons is relatively expensive.

For most (if not all) sane, modern Unixen, you can avoid the relative expense of double forking by setting SIGCHLD to IGNORE in the master, which accomplishes roughly the same goal. It states, essentially, that the parent is not interested in the result of the child's process, so don't zombify my kids waiting for me to check on them.

img

<img> resource at W3C.

internationalization

Internationalization (or "I18N") features are those being in relation to program's ability to support localized messages (see locale), currencies, and other region-specific settings.

Interchange has a rich set of I18N features that allow conditional message display, differing price formats, different currency definitions, price factoring, sorting, and other settings. Currently, those features are implemented as Interchange-specific, and do not rely on Perl's I18N/L10N features, except for the built-in POSIX support (use locale and setlocale()).

The definitions are maintained in the catalog.cfg file, through the use of Interchange's Locale directive. All settings are independent for each catalog and each user visiting that catalog; in other words, customers can access the same catalog in an unlimited number of languages, currencies and regional settings.

See locale glossary entry for more specific information.

interpolate

Default is off

interval

jobs

Interchange batch jobs can be triggered from the commandline or an Unix cronjob.

Dispatch a job from the commandline:

interchange --runjobs=training=db

Jobs are run asynchronously. The command will return before the job is completed or even started.

Directories and Files

Every job is tied to a directory containing job files. The selection of the files is controlled by the HTMLsuffix and the suffix Jobs attribute. By default all files not matching HTMLsuffix are included.

The following configuration example directs Interchange to execute all files matching jobs/NAME/*.job for the job named NAME.

Jobs base_directory jobs
Jobs suffix .job

Output and Logging

Interchange gathers the output of all job files and passes through the filter(s) specified with filter key of the Jobs directive.

The remaining output is optionally written to the job logfile and/or emailed as specified by the log and email keys of the Jobs directive.

Jobs email racke@linuxia.de
Jobs log logs/job

Tracking

Tracking jobs into a database table can be enabled with the trackdb key of the Jobs directive.

Jobs trackdb jobs

Required fields for this table are code, name, pid, begin_run, end_run.

js

levy

[Note]Note

Levy, [1913 Webster]:
3. To raise or collect by assessment; to exact by authority; as, to levy taxes, toll, tribute, or contributions.

Levies in Interchange are a way of defining multiple additional product charging such as taxes, handling, fees, and shipping. If you use Levies, the normal salestax, shipping, and handling tags are ignored (even when you use assign).

The Levy configuration directive defines individual levy sections (levy keys). The Levies configuration directive defines which levy sections will actually be honored.

Levy sections (key names) are unlimited and arbitrary; we use salestax, shipping etc. to cover most common cases, but you can put anything in there and have it work as long as the key names in Levies have a corresponding Levy definition.

Levies are a bit processor-intensive, and as such are not recalculated every time they are accessed. They can, however, be manually recalculated by using the check_status Levy parameter, as described in the Levy reference page.

link

link resource at W3C.

link program

First of all, Interchange requires a web server that is already installed on a system. Since Interchange is always running in the background as a daemon — parallel to your web server — there must be some means of communication established between the two.

For that reason, at startup, Interchange initializes the UNIX or INET domain socket (or both), depending on specific configuration. Few variants of a small CGI "link program" ship with Interchange and are intended to connect to one of those sockets. Then, they provide the data link to the web server (and consequently, to the users' browser).

[Note]Note

Since Apache is the most popular web server, further instructions will focus on Apache setup. If using another type of web server, some translation of terms may be necessary.

A ScriptAlias or other CGI execution capability is needed on the web server side, to allow use of the link program. The default ScriptAlias for many web servers is just /cgi-bin/. Otherwise if the ExecCGI Apache directive is set, then any program ending in a particular file suffix (usually .cgi) can be invoked as a CGI program.

By convention, Interchange names link programs after catalog IDs (catalog names as defined in interchange.cfg), though this is not required. Link program name is used to derive SCRIPT_PATH, which is then used to determine which Interchange catalog is referenced.

UNIX-Domain Sockets

UNIX domain sockets are not reachable from the Internet directly; they can only be accessed from the local host. This improves security and is the right thing to do when the web and Interchange server are running on the same host.

The link program vlink is the provided facility for such communication with Interchange. As said, this is the most secure way to run a catalog, as there is no way for systems on the Internet to interact with Interchange except through the offered link program.

The most important issue with UNIX-domain sockets and Interchange are file permissions (Unix sockets appear as files on the local disk). Interchange normally runs with the socket file having permissions 0600 (which translates to rw-------), which mandates that the CGI program and the Interchange server run as the same user ID. This means that the vlink program must be SUID to the same user ID as the Interchange server executes under. (Or that CGIWRAP is used on a single catalog system, but CGIWRAP is largely obsolete these days).

Even though this is against good security practice, let's say you can put SocketPerms 0666 in your interchange.cfg (and restart Interchange) to allow wide-open access to the socket file. In fact, you should do this when you have connection problems to quickly rule out whether it's just permissions or something else.

INET-Domain Sockets

INET sockets are reachable from the Internet directly. The link program tlink is the provided facility for such communication with Interchange. Other browsers can talk to the socket directly if mapped to a catalog with the global TcpMap directive. To improve security, Interchange usually checks that requests come from one of a limited number of "trusted" systems defined with the global TcpHost directive.

Setting up VLINK and TLINK

vlink and tlink, compiled from vlink.c and tlink.c source files, are very small, fast and low-overhead C programs that contact the running Interchange daemon. The vlink executable is normally made setuid (SUID) to the user account which runs Interchange, so that the UNIX-domain socket file can be set to secure permissions (user read-write only, as mentioned above). It is normally not necessary for the user to do anything — the link programs will be compiled by the configuration program. If the Interchange daemon is not running, either of the programs will display a message indicating that the server is not available.

Link program source files, found in dist/src/ directory with Interchange source tree, share the common C include file config.h which sets required variables:

  • LINK_FILE - name of the socket file (so, UNIX domain socket) that will be opened, usually /usr/local/lib/interchange/etc/socket (or anyhow, etc/socket under your ICROOT directory).

  • LINK_HOST - IP number of the host (so, INET domain socket) which should be contacted. The default of 127.0.0.1 (the local machine) is probably best for many installations.

  • LINK_PORT - TCP port number (so, INET domain socket) to connect to. The default is 7786 (the decimal ASCII codes for 'M' and 'V') and does not normally need to be changed.

  • LINK_TIMEOUT - number of seconds vlink or tlink should wait before announcing that the Interchange server is not running. The default of 45 is probably a reasonable value.

Compiling VLINK and TLINK

There is a compile_link program found within Interchange source tree that will assist you with link program compilation. Run perldoc PATH/TO/compile_link to read its documentation. (To find where the compile_link or compile_link.PL commands are, use locate compile_link or find / -name 'compile_link*').

Anyhow, you can also compile the link programs more "manually" if you position yourself to the Interchange source tree:

$ cd dist/src
$ ./configure
$ # (open and inspect config.h)
$ mkdir ../bin
$ perl compile.pl

After which the compiled vlink and tlink programs will be in your dist/bin/ directory.

For yet another iteration, if you want the control over the whole process, instead of perl compile.pl invoke simply:

$ gcc vlink.c -o ../bin/vlink
$ gcc tlink.c -o ../bin/tlink

The problem with the above approach, however, is that you might be missing critical compiler options, or that the compiler isn't gcc at all. To ensure that the C compiler will be invoked properly, use the following little ditty instead of the two gcc lines:

perl -e 'do "syscfg"; system("$CC $LIBS $CFLAGS $DEFS vlink.c -o ../bin/vlink");'
perl -e 'do "syscfg"; system("$CC $LIBS $CFLAGS $DEFS tlink.c -o ../bin/tlink");'

And finally, you can make the binaries smaller by going to ../bin/ and typing strip *, if available. (strip removes all debugging symbols from the binaries).

Have in mind that link programs are the same for all catalogs running on the same server — it's only their name that makes them refer to different catalogs. In other words, once you get one link program compiled, you can simply keep copying it to new names as you create new catalogs.

Placing VLINK and TLINK to proper system locations

First of all, if Interchange is to run under a different user account than the individual configuring the program, change vlink ownership to the Interchange user. Do not make it owned by root, because making vlink SETUID root is an huge and unnecessary security risk. It should also not normally run as the default Web user (often nobody or http>).

Here's a prospective session transcript (starting in dist/bin/):

# Set up ownership and suid bit
chown INTERCHANGE_USER vlink

# Move the vlink executable to the cgi-bin directory:
mv vlink /usr/lib/cgi-bin/CATALOG_NAME

# Most systems unset the SUID bit when moving the file,
# that's why we add suid here and not above:
chmod u+s /usr/lib/cg-bin/CATALOG_NAME

VLINK or TLINK compile problems

The latest versions of the link programs have been compiled on the following systems:

  • AIX 4.1

  • BSD2.0 (Pentium/x86)

  • Debian GNU/Linux

  • Digital Unix (OSF/Alpha)

  • FreeBSD 2.x, 3.x, 4.x

  • IRIX 5.3, IRIX 6.1, IRIX 6.5 (both SGI MIPSPro and GCC; remove -lnsl -lsocket from syscfg file on IRIX 6.x)

  • OpenBSD 2.7

  • Red Hat Linux 6.2, 7.0, 7.1, 7.2, 7.3, 8.0

  • SCO OpenServer 5.x

  • Solaris 2.x (both Sun compiler and GCC)

  • Solaris 7 (both Sun compiler and GCC)

  • SunOS 4.1.4

If nothing works and you have a problem, you can try using the tlink.pl program found in the same location as link and tlink. tlink.pl is a tlink implementation written in Perl and should work on a really broad range of systems. In any case, unless you are using tlink.pl for a specific reason, and you do have a working compiler, tuning the C source files to overcome compilation problems is preferred over using Perl implementation of the link program.

locale

See internationalization (I18N) glossary entry for a general introduction.

Localization (or "I10N") is a process of making an I18N-enabled application to use specific locale definition (messages, currency format, etc.).

Localized Catalogs

The important thing to have in mind is that Interchange allows customers to access the same catalog in an unlimited number of languages, currencies and regional settings. What's more, you can switch locales at will, at any time!

The simplest and recommended way to set the default catalog locale is to define a starting value for mv_locale in catalog.cfg:

ScratchDefault mv_locale de_DE

The per-user locale change can be made permanent (for the duration of the user session, of course), or temporary (if you're, say, displaying pricing information in multiple currencies). See setlocale for more discussion and examples.

Besides using actual "programmatic" methods to set locales, you can achieve the same effect by using one terrific feature of the process ActionMap; to display page named "pricelist" in locale fr_FR and currency en_US, simply call ITL tag [page process/locale/fr_FR/currency/en_US/page/pricelist].

Locale-specific Messages and Locale Database

The localized messages you want to display must previously be defined, of course. The simplest way to define localized messages is to use the Locale directive in any of the ways shown:

Locale de_DE "Value setting" Werteinstellung
Locale de_DE "Search"        Suchen

Locale fr_FR <<EOF
{
  "Value setting",
  "Configuration de valeur",
  
  "Search",
  "Recherche"
}
EOF

This method, however, is not practical when you have a lot of messages; for robust setups use LocaleDatabase.

With the above, to display an appropriate translation of "Value setting", you would call "[L]Value setting[/L]", which would display as "Configuration de valeur" in locale fr_FR, "Werteinstellung" in locale de_DE, and as a fallback, it would be displayed unmodified as "Value setting" — if the locale is neither fr_FR not de_DE, or no localized string for the message was found at all. Keep in mind that the "[L]" tags must be capitalized — this is done for both processing speed and easy visual distinction within pages.

[Note]A word on localization tag [L]

Interchange tries to substitute text for its localized variant very early in the page serving process — even before variables are expanded or tags interpolated.

On one hand, this means you can use variables and tags in localized strings as they will be handled properly.

On the other hand, it means code constructs like [L][item-data table field][/L] will fail, as Interchange would try to translate "[item-data table field]" literally. There's a loc tag available which is functionally identical to L, but is parsed in normal order and therefore solves the problem.

Another way to display localized messages is to supply localization text directly within Interchange pages, inside LC tag; have a look at this intuitive example:

[LC]
  This is the default text.
  [fr_FR] Text for the fr_FR locale. [/fr_FR]
  [de_DE] Text for the de_DE locale. [/de_DE]
[/LC]

It's also possible to display completely different pages, based on the locale in effect. You probably know the default HTMLsuffix in Interchange is ".html", but you probably do not know it is locale-sensitive. With a request for page named "index" ([page index]), fr_FR locale in effect, and a catalog.cfg directive of

Locale fr_FR HTMLsuffix .fr_FR

Interchange would first look for pages/index.fr_FR, and only if it's not found go to the usual pages/index.html.

[Note]A general note on locale-sensitivity of config directives

We've said HTMLsuffix is locale-sensitive, but the story gets much better. In reality, any Locale key that matches the name of a config directive, causes the directive to be re-set on locale change — in other words, all config directives are locale-sensitive!

Effect of Locales on Sorting

Interchange sorting features (such as — but not only — the sort tag) will honor a setting of LC_COLLATE, provided that the operating system and C compiler support locales for POSIX, have the locale definitions set, and the locale chosen matches one of locales available.

Suppose we have a letters database, containing some of the letters of the alphabet. A code of:

[loop 19-202 00-0011 99-102]
  [sort letters:letter]
  [loop-data letters letter]   [loop-code] <br/>
[/loop]

would provide different order for locale C than fr_FR.

MIME

MIME resource.

Minivend

macro

Interchange macros are:

  • global subroutines (GlobalSub)

  • catalog subroutines (Sub)

  • ITL code

mode

Resources at Debian Tutorial - Permissions, Debian Tutorial - Advanced Topics and O'Reilly & Associates' Essential System Administration book.

modifier

See attribute.

mv

namespace

Name-space is an abstract group of elements. Within the group, element names must be unique, but elements of the same name can appear in different namespaces (so, in different groups). They are completely separate and in no relation to each other.

Generally speaking, the namespace indicator can be prefixed to the element name (such as xsl:stylesheet), given as a separate parameter (such as name="stylesheet" space="xsl"), or set in a separate call and later considered implicit.

OS

onfly

order

Interchange sports a completely flexible item ordering scheme. One practical implementation can always be seen in the latest demo catalog shipping with Interchange; the latest demo catalog is called Standard.

Just as in many other Interchange demo catalogs, order-related files are kept in the CATROOT/pages/ord/ directory. The most important file would probably be CATROOT/pages/ord/basket.html which shows order basket contents, and is displayed to the user every time an item is added to it, or the user explicitly requests it.

[Note]Note

It is not strictly necessary to display the basket page every time an item is ordered, but most customers will be confused if you don't give them a clear visual consequence of their ordering action.

An alternative to a separate basket page could be a side-bar menu that would display cart contents in a summarized form, and be visible on all pages. However, it would still be useful to display the complete basket page to the users when they are about to stop ordering and proceed with the check-out routine.

Besides just listing electronic cart contents, the basket page usually also allows for item removal (or quantity selection in general) and total price recalculation.

In general, the body of the basket page is a HTML form. Strictly speaking, it could just be a normal list of contents (without the <form> element), but then you couldn't request any actions on the items (such as their removal from the cart).

At minimum, the basket page will contain the item-list tag that will loop over all items in the cart and display them. Any Interchange tags can be used on the page, and you can freely have multiple item lists (the basket page is in no way special and does not place any restrictions on content).

Placing Items in the Basket

Interchange supports a number of ways to add an item to the basket. You can order an item by using both form-based (button-clicking) and link-based (link-visiting) methods.

Link-based Ordering

Link-based ordering is implemented through the order tag. You should check the order reference page for complete documentation, but let's give a summary of its functionality here. In general, ITL code

[order code=SKU quantity=NUMBER href=URL cart=NAME ]Link text</a>

expands into an appropriately-constructed HTML link. Clicking a link places the item of specified SKU number in the cart and displays the basket page. The code parameter should be a valid product SKU (listed in one of the products tables), and is the only required parameter. href allows some page other than the default basket to be displayed once the item has been added to the cart. cart selects the shopping cart the item will be placed in (you see, the default cart is called main, but Interchange is always a step ahead).

The order tag supports a number of attributes (more than we've shown above), but the same effect as above can be achieved with the page or area tags as well. Here are a few lines, all equal in effect:

Order a [order TK112]Toaster</a> today!
Order a [page order TK112]Toaster</a> today!

Order a <a href="[area order TK112]">Toaster</a> today!
Order a <a href="[area href=order arg=TK112]">Toaster</a> today!

Order a <a href="[area href=order form='mv_order_item=TK112']">Toaster</a> today!

"This is all nice and well", you might say, "but how do I create the link when item data fields (such as SKU, description or price) are not known in advance?". Well, this will be the case if you're creating a flypage or displaying results based on a user search operation. The procedure is, of course, exactly the same; you could just be uncertain about where to stick in the additional ITL tags. Here's a clarifying example:

Order a [order [item-code]][item-field name]</a> today!

Form-based Ordering

Form-based ordering comes handy when you want to implement more complex ordering schemes. Consider the same ordering example as above (a toaster of product SKU TK112) implemented using forms:

<form action="[process href=order]" method="post">
  [form-session-id]
  <input name="mv_order_item"     type="hidden" value="TK112"      />
  <input name="mv_order_quantity" type="text"   value="1" size="3" /> toasters 
  <input value="Order!"           type="submit"                    />
</form>

Although the readability above suffers due to HTML elements and quoting, it's easy to dissect it to components. <form> and <input> are standard HTML elements. We care to supply type="text" with text fields for clarity, but that's the default type and can be safely omitted. What's left to note is that the <input> element names are of predetermined values, and that's only reasonable — you need to set those variables that you know Interchange will look for (otherwise this would be a waste of time). In our situation this includes setting mv_order_item (item SKU number), mv_order_quantity (item quantity) and mv_todo (desired form action). For a complete list of user mv variables, see .

You might notice we added the quantity field in the example above. From its implementation, you see that making special mv variables contain user input is extremely easy. What's more, input placeholders do not have to be a text fields as in our example; it can be a list, checkbox or a complex set of HTML widgets!

Interchange supports "stacking" of variables. This allows us to order different items ("batches") at once:

<form action="[process href=order]" method="post">
  [form-session-id]
  <input name="mv_order_item"     type="hidden" value="TK112"            />
  <input name="mv_order_quantity" type="text"   value="1"       size="3" /> Standard Toaster <br/>
  <input name="mv_order_item"     type="hidden" value="TK200"            />
  <input name="mv_order_quantity" type="text"   value="1"       size="3" /> Super Toaster
  <input value="Order!"           type="submit"                          />
</form>

Items that have a quantity of zero (or blank) will be skipped, and only items with a positive quantity will be placed in the basket.

Items created "on fly"

Note that "on the fly" items are those that do not exist in any of the products databases, and are literally "put together" on the fly. This is unrelated to Interchange flypage functionality.

If you setup the OnFly config directive, Interchange will be able to add items to user's basket even if they're not listed in any of the products databases.

A basic on-fly item can then be generated like this:

<a href="[area form="
  mv_order_item=000101
  mv_order_quantity=1
  mv_order_fly=description=An on-the-fly item|price=100.01
"]">Order item 000101</a>

Or, through a proper Submit button:

<form action="[process href=order]" method="post">
  [form-session-id]
  <input type="hidden" name="mv_order_item" value="000101">
  Qty: <input size="2" name="mv_order_quantity" value="1">
  <input type="hidden" name="mv_order_fly" value="description=An on-the-fly item|price=100.01">
  <input type="submit" value="Order button">
</form>

A lot of things might look weird in the above example, but it is all valid ITL code.

The form parameter mv_order_fly can contain any number of fields which define attributes for our on-fly item. Fields are separated by the pipe (|) character and contain value-parameter pairs separated by an equal (=) sign.

[Note]Note

To show description for the on-fly item, you must use the item-description tag; [item-field description] or [item-data products description] will not work since both try referencing existing fields in products databases.

Similarly, an attempt to generate a flypage for an on-fly item will fail, resulting in display of the SpecialPage missing URL.

Multiple items can be ordered at once by stacking the variables, as we've shown already. However, if there is only one mv_order_item instance defined, then you can stack mv_order_fly variables which will result in their concatenation just as if you separated them with pipes. Here's an example, equal in effect to the previous one:

<a href="[area form="
  mv_todo=refresh
  mv_order_item=000101
  mv_order_fly=description=An on-the-fly item
  mv_order_fly=price=100.00
"]">Order item 000101</a>

Order Groups

We've seen variable stacking at work above, but there's more to it. Imagine you had a master item and accompanying accessories which only make sense together with the main item. You could order them at once using ordinary stacking - no problem there, but what if you also wanted accessories or options removed at the same time the master item is removed from the cart? In the simplest form, this is achieved by ordering items as usual, while defining mv_order_group. The first item in the list is then treated as the master item, and all other are sub-items.

<form action="[process href=order]"  method="post">
  [form-session-id]
  <input name="mv_order_group" type="hidden" value="1"         />
  <input name="mv_order_item"  type="hidden" value="00-0011"   />
  <input name="mv_order_item"  type="hidden" value="00-0011a"  />
  <input value="Order Mona Lisa with frame" type="submit"      />
</form>

Incredible, but you can also define multiple master items! All you need to do is define a mv_order_group for each individual item. Master item "owns" all subsequent sub-items until the next master is defined.

<form action="[process href=order]"  method="post">
  [form-session-id]
  <input name="mv_order_group" type="hidden" value="1"         />
  <input name="mv_order_item"  type="hidden" value="00-0011"   />
  <input name="mv_order_group" type="hidden" value="0"         />
  <input name="mv_order_item"  type="hidden" value="00-0011a"  />
  <input name="mv_order_group" type="hidden" value="1"         />
  <input name="mv_order_item"  type="hidden" value="19-202"    />
  <input name="mv_order_group" type="hidden" value="0"         />
  <input name="mv_order_item"  type="hidden" value="99-102"    />
  <input value="Order Mona Lisa and more!"   type="submit"     />
</form>

The example shows a 4-item order form. Items 1 and 3 are master items, items 2 and 4 are sub-items. If a master item of SKU 00-0011 is deleted from the basket, 00-0011a will be deleted too. If 19-202 is deleted, then 99-102 will be deleted along.

[Note]Note

Use of HTML checkboxes for this type of thing could be inappropriate because they do not pass a value when unchecked. The value then remains defaulting to last used value. It is preferable to use radio groups or select/drop-down widgets instead. If you use checkboxes, make sure to explicitly clear mv_order_group and mv_order_item somewhere on the form page. You can re-use the following code snippet:

[value name=mv_order_group set='']
[value name=mv_order_item  set='']

mv_mi and mv_si attributes are set to the group and sub-item status of each item. The group, contained in the attribute mv_mi, is a meaningless yet unique integer. All items in a group will have the same mv_mi value. The attribute mv_si is 0 if the item is a master item, and 1 if it is a sub-item.

Basket display

Basket pages list electronic cart contents and usually allow order contents to be adjusted; items can be removed, ordered in different quantities etc.

It is possible to have an multiple (different) basket pages, because they're in no way special. It is also possible to have multiple shopping carts, for example in cases where the same user maintains a buy and sell list. Multiple basket pages could also be used where items have many accessories and, depending on the item ordered, you send customers to different, item-specific pages to choose accessories. The possibilities are endless, it's just about evaluating your actual needs.

The name of the basket page to be displayed upon item order can be defined in a few ways:

  • You can set order value of the SpecialPage directive to the page you want displayed on item order

  • You could use the order tag in form of [order code=SKU page=NAME]Order it!</a> to define the basket page

  • If you already are on the order page and want to "jump" next, use the standard form, setting some of mv_orderpage, mv_nextpage, mv_successpage and mv_failpage variables.

Multiple Shopping Carts

Interchange allows you to define and maintain multiple shopping carts for each user. The default shopping cart named main is created when the user session starts. If the user orders an item of SKU M1212 through the following link:

[order code=M1212 cart=layaway]Order this item!</a>

then the item will be placed in the cart named layaway, and not main. Consequently, the use won't see the just-ordered item on the basket page because the default shopping basket displays contents of the main cart only. The easiest way to support multiple carts is to copy the default basket page (CATROOT/pages/ord/basket.html) to a new file, insert [cart layaway] code snippet at the top, and specify it as the target page for the order tag:

[order code=M1212 cart=layaway page=ord/layaway_basket]Order this item!</a>

Now the contents of the layaway cart would be displayed on the appropriate page. Most of the fundamental cart management ITL tags support the nickname option, allowing cart name to be chosen. Using nickname is handy, and has no permanent effect, the default cart remains set at value of main. A sticky attribute could be set to change the default cart name until further notice. This is convenient, but you must remember to use [cart main] to get back to the primary cart!

And for the sake of completeness, here's the above example using form-based ordering:

<form action="[process href=order]" method="post">
  [form-session-id]
  <input name="mv_order_item"     type="checkbox" value="M1212"   /> Item M1212
  <input name="mv_order_quantity" type="text"     value="1"       /> Quantity
  <input name="mv_cartname"       type="hidden"   value="layaway" />
  <input value="order this item!" type="submit">
</form>

order check

pragma

Pragmas are used to control various aspects of page and data parsing and display. They are processed before an Interchange page goes to the normal processing routine.

Pragma values can be defined at any level; catalog-wide, page-wide or ITL-block wide.

[Note]Note

Catalog-wide pragmas are equivalent to inserting [pragma ...] on every page. However, setting the Pragma catalog directive (instead of using the pragma tag all the time) has the benefit of initializing at catalog startup time (so, only once), which avoids extra hash key lookups. Namely, there is some crossover point at about 4 Perl references where it saves processor power to assign a scalar instead of looking it up every time.

To define a pragma catalog-wide, use the Pragma directive in catalog.cfg:

Pragma NAME [=value]

To define a pragma for a particular page, use the pragma tag anywhere on a page. For readability, however, we suggest to always define pragmas at the top. The following two lines, of which you would usually use only one at a time, present pragma activation and deactivation:

[pragma NAME]
[pragma NAME 0]

To define a pragma for an ITL block inside the page, enclose the block in [tag pragma ...]:

[tag pragma NAME]1[/tag]
  ...
[tag pragma NAME]0[/tag]

Starting with Interchange 5.0, the $::Pragma->{NAME} syntax is used in the Interchange source, instead of the old $Vend::Cfg->{Pragma}{NAME}.

See the list of available pragmas in Interchange Reference Pages: Pragmas.

price

Base price of each item is kept in the products database. In fact, the three required fields for all items are code, price and description. Note that the default names are well suited for about all purposes, but you can change the actual names of the price and description columns using PriceField and DescriptionField directives.

Simple Pricing

The simplest method, as we've just hinted above, is flat pricing based on a fixed value in the products database. All you have to do is put item price in the price field and you're all set. If you want to change pricing based on quantity, size, color or arbitrary other factors, then read on.

Custom Pricing

Although the CommonAdjust directive provides a lot of options to describe pricing schemes, it might not match yours or you are just feeling more comfortable in writing code instead of delving through the following section. In this case you can simply write your own usertag calculating the price with the following settings for CommonAdjust and PriceField:

PriceField 0
CommonAdjust [calc-price]

The following example can be used to base your own usertag on. Product code and quantity for the price calculation can be found in the $item variable from the Vend::Interpolate namespace.

UserTag calc_price Routine <<EOR
sub {
	my ($code, $quantity, $set);

	$code = $item->{code};
	$quantity = $item->{quantity};

	$Tag->perl({tables => 'vendors_pricing'});

	$set = $Db{vendors_pricing}->query(q{select price from vendors_pricing where sku = '%s' order by price asc limit 1}, $code);

	if (@$set) {
		return $set->[0]->[0];
	} else {
		return 0;
	}
}
EOR

"CommonAdjust" Pricing

A flexible, chained pricing scheme is available when CommonAdjust directive is set up.

CommonAdjust string, a term we'll use in this section, will be explained below.

Here are CommonAdjust usage rules, all assuming PriceField having the default value of price.

  • If CommonAdjust is set to any value (so, be it a valid CommonAdjust string or not), extended price adjustments are enabled

  • If the price field in the products database contains a CommonAdjust string (or a non-empty, non-zero value), it takes precedence over the default set with CommonAdjust

  • If the value of the CommonAdjust directive is set to a valid CommonAdjust string, and the price field is empty or specifically 0, then the CommonAdjust string will be used to set the price of the item. An effective way to make the price field empty is to use the PriceField directive to set field name to an invalid value, such as 0, none or noprice.

  • If no CommonAdjust strings are found, then the price will be 0, and subject to any later application of discounts

  • If, as a result of CommonAdjust string application, another CommonAdjust string is found, it will be re-parsed and the result applied. Chaining is retained; a fallback may be passed and will take effect

CommonAdjust Strings, Atoms and Settors

Using valid CommonAdjust strings, prices may be adjusted in several ways, but we first need to define "CommonAdjust string" as we've promised in the introduction. CommonAdjust strings are price definition strings which consist of individual actions called atoms. Atoms in turn consist of settors. Roughly, we could say atoms convey "control information" — they define when are contained settors applied, and settors define what is applied.

Price atoms can be of type final, chained and fallback.

  1. Final price atom is applied if it does not evaluate to zero

  2. Chained price atom is subject to further adjustment

  3. Fallback price atom is skipped if a previous chained price was not zero

Atom types are recognizable from the syntax used to define them.

  • Chained atom ends with a comma

  • Fallback atom has a leading semi-colon

  • Final atoms have no comma appended or semi-colon prepended

Atoms themselves are separated by whitespace and must be quoted if they themselves contain whitespace.

A settor is a mean by which the price is set. In other words, it is an expression contained in an atom which, when evaluated, affects the item price. Just as there are different types of atoms, there are different types of settors as well. Be aware that all settor types can yield new CommonAdjust strings, and it's quite possible to create endless loops this way. Therefore, the maximum number of initial CommonAdjust strings is 16, and there may be a maximum of 32 repeated CommonAdjust string iterations or loops before the price will return zero on an error. Those limits are only defaults and can be adjusted using general-purpose Limit config directive.

[Note]Note

Common needs are easily shown but not so easily explained. Follow our pointers to examples if your vision starts to blur as you dive into the following section.

Let's take a look at available settor types. Settor types do not have distinguishable symbolic names, but their type is recognizable from the syntax used. Note that bold elements are required, while the rest are optional.

  1. -decimal

    A number which is applied to the current price value. For instance, a price of 10 and settor of 2 result in new current price of 12. May be a positive or negative decimal.

  2. -decimal%

    A number which is applied as a percentage of the current price value. May be a positive or negative number. For instance, a price of 10.00 and settor of -8% result in new current price of 9.20.

  3. table:column:key

    Causes a direct lookup in a database table. Table defaults to the products database entry for the item (subject, of course, to multiple products databases). Column argument must always be present, but you'd usually set it to price. (That scheme is often used in combination with setting PriceField to something different of price, because the price field would take precedence over the CommonAdjust string). The key defaults to the item SKU (except in a special case of attribute-based lookup).

  4. table:col1..col5,col10:key

    Causes a quantity lookup in database table which, again, defaults to products databases. Column specification is required and given as a set set of comma-separated fields (with ranges supported), looked up by the optional key. Key defaults to the item code as usual.

    Pay attention to the convenient range support; the following two settors are identical:

    pricing:p1,p2,p3,p4,p5,p10:
    pricing:p1..p5,p10:
    

    With this quantity-based lookup, item quantity is compared to the numerical portion of the column name (leading non-digits are stripped). The price is then set to a value from the appropriate database column. The "appropriateness" is determined logically — since the numeric portion of the column name determines minimum quantity for the class, Interchange picks the "highest" class for which minimum quantity requirement is fullfiled (so that the item price is set according to quantity range).

    [Warning]Warning

    If the column value at the appropriate quantity level is blank, a zero cost will be returned from the atom. It is important to have all columns populated, because 0 is not appropriate.

    One excellent extra effect you can achive with this is "mix-and-match". It allows you to order different items which you put into the same "product group" to have their quantities added together to determine the appropriate price range.

  5. ==attribute:table:column:key

    Performs attribute-based lookup. Table specifies the table for lookup. Column defaults to the name of the attribute being looked for.

  6. & PERL_CODE The leading & character is stripped and the PERL_CODE block is passed to the equivalent of a calc tag. No Interchange tags can be used in there, but the tag_data() function (data tag, basically) is available, the current price is available in variable $s, and the current item details (code, quantity, price, and any attributes) are available inside the $item hash reference. All are found in Perl package Vend::Interpolate.

    That said,

    $Vend::Interpolate::item          is the current item hash reference
    $Vend::Interpolate::item->{code}  gives key for current item
    $Vend::Interpolate::item->{mv_ib} gives database ordered from
    $Vend::Interpolate::item->{...}   gives specified attribute's value
    

  7. [ITL_CODE] or __VARIABLE__

    Causes ITL_CODE to be parsed for Interchange tags with variable substitution, but without locale substitution. You may also define a price in a Variable.

  8. $

    Causes Interchange to look in the mv_price attribute of the shopping cart, and apply that price as the final price, if it exists. The attribute must be of numerical value.

  9. >>word

    Causes Interchange to literally return word as price. This is not useful in pricing, as it will evaluate to zero. But when CommonAdjust is also used for shipping, it is a way of re-directing shipping modes.

  10. word

    The value word (which must be unique — not match any other settors) is available as a key only for the next lookup. If that next settor is a database lookup and it contains a dollar sign ($), word will be substituted for it.

  11. (settor)

    The value returned by settor will be available as key for the next lookup. If that next settor is a database lookup and it contains a dollar sign ($), word will be substituted for it.

For actual examples, please see the CommonAdjust reference page.

Product discounts

For product discount settings, see the discount glossary entry.

profile

There are different types of profiles in Interchange:

  • search profiles
  • form profiles
  • user profiles (see Profile)
  • UserDB profiles (see UserDB)

Form Profiles

Interchange form profiles are used to validate form inputs and eventually trigger additional actions. The validation usually consists in requiring that some of the fields are non-empty or match a specific regular expression pattern.

Profiles are not specific to order checkout or any specific step; they are an integral part of all form processing in Interchange. You will also see actions such as Interchange account creation, login, logout or password change being at least partly handled using profiles.

Profiles can be defined in external files (and activated using OrderProfile) or in scratch variables. External files are, by convention, kept in CATROOT/etc/ and begin with profiles.. Multiple profiles can be defined in each file.

A very simple hello world-like profile example follows:

__NAME__  test_profile

fname=required
lname=required

__END__

The above profile requires customers' first and last names to be entered.

[Note]Note

The __NAME__ and __END__ markers must start at the beginning of line, not even whitespace is allowed. This is very important because the profile would otherwise be silently ignored.

If users leave some of the required fields empty (or there's some other reason why you'd want them to correct their input), you'll probably want to show them the form page again and display some kind of an error message. To override the default messages, simply specify your own strings after the format check specification. Here's an example:

__NAME__  test_profile

fname=required First name is required!
lname=required Last name is required!

__END__

Format-check Specification

From the examples above, you can see that the format check specifications are specified as FORM_VARIABLE_NAME=CHECK_TYPE. Form variable names are obvious; in our example they were fname and lname. Check types, however, can take on one of the following values:

  • required - form field must end up with non-empty content. If no direct CGI variable exists, variable is searched in the the values space (form fields submitted at some past time during the session) and UserDB

  • mandatory - form field must contain a non-blank value, and it must exist directly on the form being checked. In other words, it must be a CGI variable and not a value variable coming from some previous form input or UserDB

  • phone - form field must look like a phone number (by a very loose specification), allowing numbers from all countries

  • phone_us - form field must have US phone number formatting with area code included

  • state - form field must contain an US state, including DC and Puerto Rico

  • province - form field must contain a canadian province or pre-1997 territory

  • state_province - form field must contain an US state or canadian province

  • zip - form field must contain an US postal code formatting, with optional ZIP+4. This is also called by the alias us_postcode

  • ca_postcode - form field must contain a canadian postal code formatting (check for a valid first letter is performed)

  • postcode - form field must contain a valid US or Canada postal code formatting

  • true - form field content must begin with y, 1 or t (case-insensitive)

  • false - form field content must begin with n, 0 or f (case-insensitive)

  • always_pass - form field always passes. This can be used to update the field regardless of the value

  • email - form field content must pass rudimentary e-mail address check; it must contain "AT" (@), a name, and a minimal domain

  • regex REGEX_PATTERNs - form field content must match regular expression patterns. Multiple patterns can be specified, separated by whitespace

  • length RANGE - form field content must satisfy the allowed length range

  • match CGI_VARNAME - form field content must be equal to the content of another field, CGI_VARNAME

  • unique DATABASE - form field content must not exist in a given DATABASE

  • filter FILTER - form field content should be equal to the original value after being ran through the specified FILTER

Chained Checks

Checks can be combined with the &and and &or profile operators. The following example uses the <check>regex</check> and <check>length</check> checks to ensure that the input is suitable for system usernames and is within certain length bounds.

username=regex ^[a-z][-a-z0-9]*$ "Invalid characters in username."
&and
username=length 6-32 Size limits exceeded (6-32 characters)

reconfigure

Most of the time, the changes you make to the HTML files or databases are directly visible on the next access to your site. However, that is not the case with more "serious" files such as configuration files (catalog.cfg) or profiles (etc/profiles*).

To apply changes from all those "non-instant" files, you must restart Interchange server or — quicker and more elegant — reconfigure just the specific catalog.

Changes to global configuration file (interchange.cfg) can only be applied by restarting Interchange completely.

For practical instructions, see .

regexp

reparse

Default is on.

See the section called “interpolate”.

robot

rotated banner

In Interchange, the term "Rotated Banner" refers to the process of selecting or displaying one of the banners from the banner field in the banner database, when that field contains multiple banners (separated by appropriate delimiters) and the rotate field contains any positive integer value.

The available banners are displayed in sequential order, with an independent pointer for each client.

See the banner tag and for further discussion and examples.

SDBM

SDBM is an in-file, non-SQL database (similar to GDBM). Interchange will, however, add an SQL layer on top of every supported non-SQL database so SQL calls can be used, as long as they are made through Interchange.

Interchange must work with some kind of a database. When no SQL database is specified, database source files (text) are still stored in a database, even though it's a file-based one (GDBM or similar) and requires no special setup from the user.

For in-file database backend, Interchange will either use GNU DBM or Berkeley DB, depending on what it finds on the system.

Yet another option was SDBM, but it is severely handicapped — it does not support values bigger than 2048 bytes and at least two of our demo catalog databases fail to load for that reason.

Since early 2006, Interchange no longer considers SDBM an option, unless it is explicitly told so. You can explicitly instruct Interchange to use SDBM backend for particular databases, but that is not encouraged and SBDM should not be used.

SKU

Stock Keeping Unit.

SMTP

SMTP resource.

Net::SMTP Perl module (follow the "libnet" link).

SOAP RPC

Basic setup

To enable the Interchange SOAP server, set SOAP to yes in the global configuration file. It is also important to allow SOAP actions with SOAP_Control both in the global configuration and in the catalog configuration file in order to provide web services.

SOAP_Control Action always

After restart, the Interchange SOAP server will listen on port 7780 for requests.

For all catalogs providing SOAP services, do the following steps:

  1. Enable SOAP and allow SOAP actions in the catalog configuration file:

    SOAP Yes
    SOAP_Control Action always
    

  2. Add SOAP URL to Catalog directive in the global configuration file:

    Catalog wellwell /home/interchange/catalogs/wellwell /cgi-bin/wellwell /soap
    
  3. Define SOAP actions with SOAP_Action.

Finally, restart your Interchange server.

Advanced setup

The port can be changed with the SOAP_Socket configuration directive.

Faults

If Interchange cannot determine the catalog for the SOAP request, it will trigger the soap:Client.NotFound fault.

If the catalog hasn't enabled SOAP, Interchange will trigger the soap:Client.NotAvailable fault.

If the called SOAP action dies, Interchange triggers a soap:Server fault with the message Application error.

SOAP references

SOAP resource at W3C.

SOAP::Lite homepage.

SQL

SSL

Secure Sockets Layer resource at Wikipedia.

Interchange has several features that enable secure ordering via SSL. Despite their mystique, SSL web servers are actually quite easy to operate. The difference between the standard HTTP server and the SSL HTTPS server, from the standpoint of the user, is only in the encryption that happens kind-of transparently, and the specification of the URL -- https: is used for the URL protocol specification instead of the usual http: designation.

[Note]Note

Interchange attempts to perform operations securely, but no guarantees or warranties of any kind are made! Since Interchange comes with Perl source, it is possible to modify the program to create bad security problems. One way to minimize this possibility is to record digital signatures, using MD5 or PGP, of bin/interchange, interchange.cfg, and all modules included in Interchange. Then verify them on a regular basis to ensure they have not been changed.

Interchange uses the SecureURL directive to set the base URL for secure transactions, and the VendURL directive for normal non-secure transactions. Secure URLs can be enabled for individual forms through a form action of [process secure=1]. An individual page can be displayed via SSL with [page href=PAGE_URL secure=1]PAGE_NAME</a>. A certain page can be set to always be secure with the AlwaysSecure directive.

Interchange incorporates additional security for credit card numbers. The field mv_credit_card_number will not ever be written to disk.

To enable automated encryption of the credit card information, you need to enable CreditCardAuto. EncryptProgram also needs to be set to a command which will, with hope, encrypt the number when invoked. PGP is now recommended above all other encryption program. The entries should look something like:

  CreditCardAuto Yes
  EncryptProgram /usr/bin/pgpe -fat -r sales@company.com

See CreditCardAuto, PGP, GPG_PATH, EncryptKey and EncryptProgram configuration directives for more information and examples.

safe

salt

In password protection, salt is a random string of data (two characters, in traditional crypt implementations) used to modify a password hash.

Salt is added to make it impossible (or at least more difficult) for an attacker to break into a system by pre-compiling large dictionaries of password strings and then comparing them to the crypted strings from the password files. In effect, adding a 2-character salt to a password hash makes more than 600 possible combinations of the same actual password.

One of the side effects of salt is that it causes an actual password to generate two different encrypted strings (when two different salts are used), so multiple users can accidentally choose the same password without making that fact obvious to any user with read access to the crypted strings.

scalar

scratch

When Interchange creates a user session, it also initializes the scratch space (also referred to as the scratchpad) to hold various variables which are valid throughout the session.

By default, the scratch space is created empty. You can define default scratch variables and their values using ScratchDefault.

Once defined, a scratch variable will exist either until it is explicitly deleted (most probably using scratchd tag) or the session ends. The exception are so-called "temp" variables, which are normal scratch variables (they live in the scratch space), but are automatically deleted when page processing ends. You can use them for saving intermediate results that you calculate and display, and then want Interchange to automatically forget.

The catalog programmer has complete control over the scratch variables. The following tags manipulate the scratch space: set, seti, tmp, tmpn, scratch and scratchd.

Here's a complete list of ways to set scratch variables.

In ITL:

Set syntaxAttributes
[set VARNAME]VALUE[/set]ITL code in value body is not interpolated
[seti VARNAME]VALUE[/seti]ITL code in value body is interpolated
[tmp VARNAME]VALUE[/tmp]ITL code in value body is interpolated, variable is temporary
[tmpn VARNAME]VALUE[/tmpn]ITL code in value body is not interpolated, variable is temporary

In embedded Perl:

Set syntaxAttributes
$Scratch->{VARNAME} = 'VALUE';Set value
$Tag->tmp('VARNAME');Mark scratch variable as temporary, must set a value afterwards
$Tag->tmp('VARNAME', 'VALUE');Mark scratch variable as temporary and set value at the same time

In GlobalSub code or usertags:

Set syntaxAttributes
$::Scratch->{VARNAME} = 'VALUE';Set value

Here's a complete list of ways to get scratch variables.

In ITL:

Get syntaxAttributes
[scratch VARNAME]Display value
[scratchd VARNAME]Display value, delete scratch variable

In embedded Perl:

Get syntaxAttributes
$Scratch->{VARNAME};Get value
$Session->{scratch}{VARNAME};Equivalent

In GlobalSub code or usertags:

Get syntaxAttributes
$::Scratch->{VARNAME};Get value
$::Session->{scratch}{VARNAME};Equivalent

One other predefined use for scratch variables is to hold form processing code (code that is executed on users' form submission). See the button tag for examples.

session

The session contains:

  • cart contents
  • CGI variables
  • value variables
  • errors
  • warnings
  • discount settings
  • levy charges
  • input filters in the Filter key, see input-filter
  • captcha code in captcha key
  • result of subtotal calculation in latest_subtotal (unless called with nodiscount=1)

The length of the session identifier is determined by the session_id_length Limit setting.

shipping

special

static

tab delimited

table

See database.

tarball

tax

Interchange allows taxing in a number of ways.

Simple "salestax.asc" Method

Sales tax calculation in this simple scheme is performed on a straight percentage basis, with certain items allowed to be tax-exempt. Simply initialize the SalesTax directive to the name of lookup fields. Those lookup fields are the ones that are available on the final order form and indicate geographical locality.

SalesTax configuration directive is set to form fields whose values will be used as keys to look up the tax rate in products/salestax.asc, based on localities. salestax is a fixed-format database, and consists of identifiers that match localities defined using SalesTax.

Locality names are always forced to UPPER CASE, and are usually identified by zip or state codes:

SalesTax    zip,state

Each line of the mentioned products/salestax.asc file should contain a code (again, usually 5-digit zip or 2-letter state ID), followed by a TAB character and a desired percentage.

default	0.0
45056	.0525
61821	.0725
61801	.075
IL	.0625
OH	.0525
VAT	.15
WA	.08

Based on user information given on the order form (and given our sample SalesTax setting), Interchange will attempt a tax lookup by zip and state (in that order), and apply the percentage found to the subtotal of the order. The subtotal will include item prices, taxes and shipping costs (if TaxShipping was set up). It will add the percentage, then make that available with the salestax tag for display on the order form. If no match is found in CATROOT/products/salestax.asc, the entry DEFAULT (case-insensitive) will be applied — which is usually zero.

If some items are not taxable, then you must set up a field in your database which indicates that. Announce this field name by using the NonTaxableField directive. If the specified field for that item evaluates to a true value, sales tax will not be applied to the item.

If you want to set a fixed tax for all orders, as might occur for VAT in some countries, then SalesTax set to the zip or state codes is not optimal for the purpose of sales tax calculation. The solution is to introduce an arbitrary variable that, unlike zip or state, does not change from user to user. Then, you would set that variable in user session to point to a fixed entry in the CATROOT/products/salestax.asc file. Exactly how you're going to set a session variable is not important; you could use the ValuesDefault directive ( ValuesDefault tax_code VAT), a hidden form variable (<input type="hidden" name="tax_code" value="VAT" />), or a simple Perl code block ([perl] $Values->{tax_code} = 'VAT'; return [/perl]).

"Fly Tax" Method

This is one of the simpler taxing methods as well. A series of Interchange Variable settings are read to develop a salestax rate for one or more geographical localities.

With this method, you implement the simple SalesTax method explained above, but only put one entry in CATROOT/products/salestax.asc. Then , TAXRATE and TAXSHIPPING catalog variables would be used to build the tax rates.

The single entry in CATROOT/products/salestax.asc should be default with a value of fly-tax:

DEFAULT	[fly-tax]

"Salestax Multi" or "VAT Taxing" Method

With this method, country and state databases are used to develop complex VAT or salestax rate calculations, based on country and state, possibly with different rates based on product type.

The SalesTax directive must be set to multi, and then the type of tax to apply will be read from the country database. Since this is a standard database, to display taxing for say, Croatia (code HR), you'd simply call:

[data table=country col=tax key=HR]

We've mentioned some hard-coded values above (the country table, column names etc.), but this is all configurable using MV_COUNTRY_TABLE, MV_COUNTRY_FIELD, MV_COUNTRY_TAX_FIELD, MV_STATE_TABLE, , MV_STATE_TAX_FIELD, MV_TAX_TYPE_FIELD and MV_TAX_CATEGORY_FIELD variables.

So, with this multi approach, Interchange first performs a lookup in the country database. The default key for the lookup is, of course, [value country] (value of the country form variable), and the column retrieved is tax. At that point, the following rules are applied:

  • If no string is found, tax returns 0

  • If string simple:COUNTRY_CODE is found, fly-tax is used for the appropriate country.

  • If string state is found, then another lookup in the country database is done; it's something along the lines of

    select tax from state where country = '[value country]' and state = '[value state]'
    

    The value is then applied as explained in the following steps

  • If a pure (integer or decimal) number is found (such as 0.05), the rate is applied directly

  • If a percentage is found, such as 22.2%, the rate is, obviously, applied as a percentage

  • If a string category = D%, default = D% is found, the tax_category field in the products database is used to determine tax rate. (The default option is there to be applied if the tax_category for a product is zero or unspecified. The special field mv_shipping is used to set tax rate for shipping, if shipping is indeed taxed. If shipping is taxed only when taxable items are in the cart, then use mv_shipping_when_taxable instead.)

"Levies" or "Multiple Tax" Method

Using Levies and Levy structure, any or all of the above methods are used in combination to implement the a taxing scheme of truly arbitrary complexity.

Conclusion

To prevent tax calculation routines from ever applying a negative tax amount, see pragma <pragma>no_negative_tax</pragma>.

time

Time specification follows:

Table 1. Recognized format strings for time functions

Format StringDescriptionRangeExample
%cdate and time Mon Apr 12 14:17:46 2004
%xdate 01/18/2004
%Xtime 14:17:46
%Y %yyear 2004 04
%jday of the year0-366112
%mmonth01-1201
%B %bmonth name January Jan
%d %eday of the month01-3108 8
%U %Wweek number, week starts Sunday (%U) or Monday (%W)00-5348 47
%w %uweekday0-603 3
%A %aweekday name Sunday Sun
%H %Ihour00-23 00-1223 11
%pAM/PM PM
%Mminute00-5957
%sseconds since epoch 1233908038
%Ssecond00-5957
%Ztime zone name CEST


tracking

Several actions performed by users are recorded by Interchange's usertracking facility, if one is enabled. Usertrack data can actually be obtained from two different locations.

The first location is Interchange itself, where you tune the TrackFile directive to your needs (such as logs/usertrack). This is the approach used in the standard demo.

The second location are the HTTP headers. You can configure the Web server to write this header into access logs; here's an example for the Apache web server:

LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \
			%T %v \"%{X-Track}o\"" track
CustomLog /var/log/apache/track.log track

true

In general programming terms, a true value is anything that is not false.

Note that a string containing only whitespace (say, one TAB or space) is also considered 'true'.

UI

UserDB

The name "UserDB" stands for a set of built-in Interchange features related to the user database. That includes the database table that will hold your visitors data, and the set of functions operating either on that database or on people's session information.

Interchange's set of user database functions is elaborate, but it still manages not to place application-specific requirements on your practical database. While it is possible to use Interchange's UserDB functions with your custom type of database, it is strongly recommended to follow simple UserDB naming and usage standards. That alone will instantly give you a huge amount of built-in userdb functionality with no overhead programming.

The whole UserDB set consists of:

  • The user database. It is the database table that will actually contain all persistent information for visitors who create an account at your website. This table has only a few required fields.

  • Set of predefined Interchange variables (such as mv_username, mv_password and mv_verify.) As you will see, most of the UserDB functions in Interchange will be simplified to placing data in predefined variables and then invoking required actions; no matter how complex the actions might be internally.

  • The userdb tag. This tag is the convenient method of invoking UserDB functions. As long as you will adhere to naming and usage standards, UserDB invocations will be as simple as [userdb login], [userdb save] or [userdb logout].

Creating the user database

To create the database, you could follow a very simple structure. The only two required fields are username and password. Your sample test database could look like this:

code	password

(Note that the name of the first column — the primary key — is arbitrary because Interchange's default functions are not accessing it by name. But it is a good standard to name the first field code in all your database tables).

It is, however, recommended to use an SQL database for any more complex functions. Let's take a look at a more elaborate user database that, among other things, has support for visitors' first and last name, inactivity flag and billing and shipping addresses:

Database  userdb  LENGTH_EXCEPTION_DEFAULT  truncate_log
Database  userdb  DEFAULT_TYPE  varchar(255)

Database  userdb  COLUMN_DEF   "username=varchar(64) NOT NULL PRIMARY KEY"
Database  userdb  COLUMN_DEF   "password=varchar(64) NOT NULL"
Database  userdb  COLUMN_DEF   "accounts=text"
Database  userdb  COLUMN_DEF   "acl=text"
Database  userdb  COLUMN_DEF   "address1=varchar(64)"
Database  userdb  COLUMN_DEF   "address2=varchar(64)"
Database  userdb  COLUMN_DEF   "address3=varchar(64)"
Database  userdb  COLUMN_DEF   "address_book=text"
Database  userdb  COLUMN_DEF   "b_address1=varchar(64)"
Database  userdb  COLUMN_DEF   "b_address2=varchar(64)"
Database  userdb  COLUMN_DEF   "b_address3=varchar(64)"
Database  userdb  COLUMN_DEF   "b_city=varchar(30)"
Database  userdb  COLUMN_DEF   "b_company=varchar(64)"
Database  userdb  COLUMN_DEF   "b_country=varchar(10)"
Database  userdb  COLUMN_DEF   "b_fname=varchar(30)"
Database  userdb  COLUMN_DEF   "b_lname=varchar(30)"
Database  userdb  COLUMN_DEF   "b_nickname=text"
Database  userdb  COLUMN_DEF   "b_phone=varchar(30)"
Database  userdb  COLUMN_DEF   "b_state=varchar(10)"
Database  userdb  COLUMN_DEF   "b_zip=varchar(10)"
Database  userdb  COLUMN_DEF   "carts=text"
Database  userdb  COLUMN_DEF   "city=varchar(30)"
Database  userdb  COLUMN_DEF   "company=varchar(64)"
Database  userdb  COLUMN_DEF   "country=varchar(10)"
Database  userdb  COLUMN_DEF   "credit_limit=varchar(16)"
Database  userdb  COLUMN_DEF   "db_acl=text"
Database  userdb  COLUMN_DEF   "dealer=varchar(32)"
Database  userdb  COLUMN_DEF   "email=varchar(42)"
Database  userdb  COLUMN_DEF   "fax=varchar(30)"
Database  userdb  COLUMN_DEF   "file_acl=text"
Database  userdb  COLUMN_DEF   "fname=varchar(30)"
Database  userdb  COLUMN_DEF   "inactive=varchar(8)"
Database  userdb  COLUMN_DEF   "lname=varchar(30)"
Database  userdb  COLUMN_DEF   "mail_list=text"
Database  userdb  COLUMN_DEF   "mod_time=varchar(20)"
Database  userdb  COLUMN_DEF   "mv_shipmode=varchar(255)"
Database  userdb  COLUMN_DEF   "owner=varchar(20)"
Database  userdb  COLUMN_DEF   "p_nickname=text"
Database  userdb  COLUMN_DEF   "phone_day=varchar(30)"
Database  userdb  COLUMN_DEF   "phone_night=varchar(30)"
Database  userdb  COLUMN_DEF   "price_level=varchar(30)"
Database  userdb  COLUMN_DEF   "preferences=text"
Database  userdb  COLUMN_DEF   "s_nickname=text"
Database  userdb  COLUMN_DEF   "state=varchar(20)"
Database  userdb  COLUMN_DEF   "zip=varchar(10)"
Database  userdb  DEFAULT      "inactive=''"

The above is Interchange's database representation format that allows Interchange to be aware of the database structure and automagically create it if it's missing. See database glossary entry for complete information.

The above is PostgreSQL-compatible definition. We have the equivalent MySQL version available (in short, you only need to replace type =text with MySQL's =BLOB).

Registering the database

To make the new database table accessible to Interchange, and to define a couple more UserDB-specific options, you could add the following to your catalog.cfg:

# For SQL databases:
Database  userdb  userdb.txt   __SQLDSN__

# Or for file-based database (DBM):
Database  userdb  userdb.txt   TAB

# Encrypt passwords?
UserDB    default    crypt         0

# Ignore uppercase/lowercase in usernames?
UserDB    default    ignore_case   1

# Enable this in combination with the above, so that
# username is always 'normalized':
Filter    mv_username lc

# To disable field containing date of last change:
UserDB    default    time_field    none

# To enable field containing date of last change
#UserDB    default    time_field    mod_time

UserDB    default    logfile       var/log/userdb.log

# To allow people login using their email, and not their username
# (in that case, username does not have to be meaningful and
# can be automatically assigned, like "U00001"):
#UserDB    default    indirect_login  email

Creating the login page

The login page could again be very simple, like this:

[set Login]
[userdb login]
[/set]

<form action="[process secure=1]" method="POST">
  <input type="hidden" name="mv_todo"  value="return">
  <input type="hidden" name="mv_nextpage" value="index">
  <input type="hidden" name="mv_click" value="Login">
  [form-session-id]

  <input name="mv_username" type="text">
  <input name="mv_password" type="password">
  <input value="Submit"   type="submit">
</form>

More complex login page with support for cookies and "remembering" users, and that displays a logout option if the user is already logged in could look like this:

[set Login]
  [userdb login]
[/set]

[set Logout_choice]
  [if type=explicit compare="[userdb function=logout clear='[cgi clear_values]']"]
    [set mv_no_count]1[/set]
    [set mv_no_session_id]1[/set]
    [if cgi clear_cart]
      [calc] @$Items = (); return; [/calc]
    [/if]
  [/if]
[/set]

[tmp cookie_username][read-cookie MV_USERNAME][/tmp]

<form action="[process secure=1]" method="POST">
  <input type="hidden" name="mv_todo"  value="return">
  <input type="hidden" name="mv_nextpage" value="index">
  [form-session-id]

  [if !session logged_in]
    <input type="hidden" name="mv_click" value="Login">

    <input name="mv_username" type="text">
    <input name="mv_password" type="password">

    [if config CookieLogin]
    <input type="hidden" name="mv_cookie_password" value="0">
    <input type="checkbox" name="mv_cookie_password" value="1" [if scratch cookie_username]CHECKED[/if]>
    [/if]

  [else]
    <input type="hidden" name="mv_click" value="Logout_choice">

    Logged in as [data session username].

    <input class="input" type="hidden" name="clear_values" value="1">
    <input class="input" type="checkbox" name="clear_values" checked value="1"> Erase values.

  [/else]
  [/if]

  <input type="submit" value="Submit">
</form>


Conclusion

When the user logs in, user database values are automatically copied to their values space and can be retrieved at any time using the value tag. (Values which are not present in the database might take on a default value defined with ValuesDefault).

Often times, you would like to save users' data back to the user database, be it during or at the end of user session. You can automatically do this with a combination of form processing directives, but if you want to do it manually, use the following simple yet powerful code:

[update values]
[userdb save]

The above code saves all users' values back to the database. Values which do not have a corresponding database field are ignored (as there's no place to save them). This is a fault-tolerant behavior and something you almost always want to happen anyway.

For all advanced examples and more technical discussion, see userdb tag documentation.

umask

usertag

Tag is a basic functional unit in ITL — Interchange Tag Language. It is to Interchange what HTML tags are to a HTML page, or binary executables to an Unix shell.

As tags and their usage are explained under the ITL glossary entry, we are going to explain usertag inclusion in the Interchange server and usertag programming here.

Usertag inclusion

Usertag programming

Place of definition

Tags have traditionally been defined in lib/Vend/Interpolate.pm file in the Interchange source tree. While some of the crucial tags are still defined there (search for ^sub tag_ in the file) to solve the chicken-or-egg problem, new tags should be created as standalone files within the code/ directory in the Interchange source. This makes them more manageable and allows you to easily "deactivate" unused tags and decrease IC;'s memory footprint (as explained above).

Tag types

Even though all Interchange tags are generally called tags or usertags, there are actually three types of tags: system tags, user tags and UI tags. There's no functional difference between them, but we've decided to introduce a rough distinction.

  • System tags (or core tags) are defined in lib/Vend/Interpolate.pm and code/SystemTag/ in Interchange source. They are used by core Interchange modules (lib/Vend/*.pm files) and are required for a functional installation. Some files have the extension .coretag, and some have the usual .tag, but there's no difference. (.tag is preferred for your custom tags).

  • User tags are defined in code/UserTag/ directory and form a collection of commonly used Interchange tags. This is the most common type and directly intended for custom catalog programming.

  • UI (User Interface) tags are defined in code/UI_Tag/ directory and form a collection of extra tags used by our Admin UI interface.

A catalog that is not running the Admin UI should, theoretically, be able to do without the whole code/UI_Tag/ directory. However, as very useful tags are found within all three types, the AccumulateCode approach is preferred over this crude directory-based selection.

Tag restrictions and global code

Global usertags (defined at the Interchange server level) run directly under Interchange server permissions, without restrictions.

Catalog usertags (defined at the catalog level), however, run under safe restrictions to maximize security.

You should run all your custom tags at catalog-level and eventually let some of the restrictions loose using SafeUntrap configuration directive. Run global usertags only when there is no other option, and make sure your code is as resilient to arbitrary user input as possible.

Location for custom usertags

Usertags are defined using the UserTag config directive so, obviously, they have to be defined in interchange.cfg or catalog.cfg, or files included by those basic configuration files.

While you could add your own tags to the default Interchange directories (within the code/ directory, as explained) or even define them in interchange.cfg or catalog.cfg directly, it's generally best if you create a usertag/ directory (at a catalog or global level), put your custom tags there, and include them in the running configuration with the include usertag/*.tag configuration directive.

Basic usertag file

Here's a classic hello world usertag example, containing all the relevant structural elements:

# Copyright YEAR COPYRIGHT-HOLDER-NAME EMAIL-OR-WEB-ADDRESS
# Licensed under the GNU GPL v2. See file LICENSE for details.
# $Id: usertag,v 1.4 2007-11-14 12:38:08 racke Exp $

UserTag hello-world Order     name
UserTag hello-world addAttr
UserTag hello-world Version   $Revision: 1.4 $
UserTag hello-world Routine   <<EOR
sub {
  my ($name, $opt) = @_;
  my $ret;

  $name ||= "world";
  $name = ucfirst $name;

  if ( $_ = $opt->{surname} ) {
    $_ = ucfirst;
    $name .= " $_";
  }

  $ret = "Hello, $name!";

  return $ret;
}
EOR

After you install the usertag (as explained above), you can test it by using this sample HTML code:

<pre>
  The default name: [hello-world]

  Name "John": [hello-world john]

  Name "John", surname "Doe": [hello-world name=john surname=doe]
</pre>

As you can see, each usertag is defined through a series of UserTag lines. All possible UserTag options are explained in the following section.

Usertag options

Recognized usertag options are defined as a Perl hash named %tagCanon in file lib/Vend/Config.pm:

  • Group

  • ActionMap

  • ArrayCode

  • HashCode

  • CoreTag

  • SearchOp

  • Filter

  • FormAction

  • OrderCheck

  • UserTag

  • SystemTag

  • Widget

  • Alias — another name, an alias, for the tag.

    UserTag ALIASED-NAME Alias NAME
    
    UserTag time Version $Revision: 1.4 $
    UserTag date Alias   time
    

  • addAttr — pass a hash reference with all user-supplied tag attributes as last argument to the tag handling subroutine.

    UserTag NAME addAttr [VALUE]
    
    UserTag benchmark addAttr     (implies Yes)
    
    UserTag benchmark addAttr 1
    
    UserTag benchmark addAttr 0
    

  • attrAlias — another name, an alias, for a tag's attribute.

    UserTag NAME attrAlias ALIASED-ATTR-NAME REAL-ATTR-NAME
    
    UserTag meta-info Order     table column key
    UserTag meta-info attrAlias col column
    

  • attrDefault

  • canNest

  • Description — embedded, one-line tag description.

    UserTag NAME Description TEXT
    
    UserTag uninstall_feature Description Uninstall feature installed with 'Feature' config directive.
    
    UserTag uninstall_feature Description <<EOD
    Uninstall feature installed with
    'Feature' config directive.
    EOD
    

  • Override

  • Visibility

  • Help

  • Documentation — embedded tag documentation. This can be any free-form text, but sometimes it's handy to write the documentation in Perl POD syntax, as it allows the use of convenient pod2text and related commands to read the documentation.

    UserTag NAME Documentation TEXT
    
    UserTag tabbed-display Documentation <<EOD
    tabbed-display -- DHTML tabbed display
      ......
    EOD
    

  • ExtraMeta

  • Gobble

  • hasEndTag — the tag has an end tag. In other words, the tag is a container.

    UserTag NAME hasEndTag [VALUE]
    
    UserTag widget hasEndTag     (implies Yes)
    
    UserTag widget hasEndTag 1
    
    UserTag widget hasEndTag 0
    

  • Implicit

  • Interpolate — interpolate tag data. Due to a poor naming choice, this option behaves differently for non-container and container tags.

    For non-container tags, it specifies whether tag output should be reparsed for more Interchange tags.

    For container tags, it specifies whether tag body should be interpolated before being passed to the tag. Another option, NoReparse then controls whether final tag output should be reparsed for more Interchange tags.

    Interpolation is turned off by default.

    UserTag NAME Interpolate [VALUE]
    
    UserTag table-organize Interpolate     (implies Yes)
    
    UserTag table-organize Interpolate 1
    
    UserTag table-organize Interpolate 0
    

  • InvalidateCache

  • isEndAnchor

  • noRearrange

  • Order

  • PosNumber — number of positional tag parameters. This option is not required as the number is automatically calculated from the Order option.

    UserTag NAME PosNumber COUNT
    
    UserTag test Order     opt1 opt2 opt3
    UserTag test PosNumber 3
    

  • PosRoutine

  • MapRoutine

  • NoReparse — do not reparse output from container tags for more Interchange tags. This option has no effect on non-container tags.

    Reparsing is turned on by default (NoReparse 0).

    UserTag NAME NoReparse [VALUE]
    
    UserTag either NoReparse     (implies Yes)
    
    UserTag either NoReparse 1
    
    UserTag either NoReparse 0
    

  • JavaScriptCheck

  • Required

  • Routine

  • Version

value

variable

In general terms, a variable is "that which is variable; that which varies, or is subject to change".

In computer terms, variables have a name most of the time, so that you can refer to them. For example, variable $amount might have a value of 10, and that value may change over time.

Basically, all programs work with (a lot of) variables, be it to define their behavior or store intermediate results.

In Interchange, there are many kinds of variables. We have:

Read the respective glossary entries. This entry only deals with global and catalog variables.

Global and catalog variables are basic ways of storing variable information in Interchange. They are basically the same, but some of the definitions only make sense at global (Interchange server) level, and some only make sense at "local" (individual catalog) level. There's also a fallback mechanism available that can query the global setting if its instance at catalog level is not set.

Of those variables, we further (informally) distinguish between "core", "distribution" and "standard" variables. Core variables are being honored by common, underlying Interchange code; distribution variables are honored in our out of the box configurations, and standard variables are being honored in our standard demo catalog that we ship along with Interchange.

Look up the Variable configuration directive for instructions on setting variable values. Look up the var tag for basic instructions on getting variable values.

Global and catalog variables are not normally modified dynamically (they keep their value as set in interchange.cfg or catalog.cfg). However, they can be manipulated at runtime, in which case you most probably want to do it before Interchange puts a requested page into processing. This is best done in an Autoload routine.

[Note]Note

By the way, an Autoload routine is what gives your catalog unlimited flexibility. It made my head spin in disbelief when I first saw some of the Autoload tricks, and it continues to amuse me to this day. If you want to take a quick break from problem at hand, see configuration glossary entry for an explanation of what happens with configuration directives on each page request, and Autoload reference page for Autoload-specific discussion and examples.

When accessing variables, we distinguish between three access types: from ITL code, from embedded Perl code, and from GlobalSubs or tags.

Here's a complete list of ways to access global or catalog variables:

In ITL:

Access syntaxPlace of definition
__VARNAME__catalog.cfg
@_VARNAME_@catalog.cfg, with fallback to interchange.cfg
@@VARNAME@@interchange.cfg
[var VARNAME]catalog.cfg
[var VARNAME 1]interchange.cfg
[var VARNAME 2]catalog.cfg, with fallback to interchange.cfg

In embedded Perl:

Access syntaxPlace of definition
$Variable->{VARNAME}catalog.cfg
$Tag->var('VARNAME')catalog.cfg
$Tag->var('VARNAME', 1)interchange.cfg
$Tag->var('VARNAME', 2)catalog.cfg, with fallback to interchange.cfg

In GlobalSub code or usertags:

Access syntaxPlace of definition
$::Variable->{VARNAME}catalog.cfg
$Tag->var('VARNAME')catalog.cfg
$Tag->var('VARNAME', 1)interchange.cfg
$Tag->var('VARNAME', 2)catalog.cfg, with fallback to interchange.cfg
$Global::Variable->{VARNAME}interchange.cfg, and only within GlobalSub code

weighted

widget

DocBook!Interchange!