=head1 NAME

MiniVend -- shopping cart and electronic catalog system

=head2 Version

This document describes MiniVend 4.0, based on Andrew Wilcox's
original Vend, Version 0.2, with portions from Vend 0.3. This is
the fourth major revision of MiniVend.

=head1 DESCRIPTION

MiniVend is a database access and retrieval system focused on e-commerce.
It allows customers to select items to buy from catalog pages. The program
tracks which products they have selected and the quantity desired. Many
different catalog pages may be visited, and the user "session" will be
tracked to build a cumulative list of items. Once selection is finished,
they may complete the ordering process by entering their name and
address along with payment information, if any. Once the order process
is completed, MiniVend submits the order to the system via email or an
external order entry program.

Though its name begins with "Mini", MiniVend is anything but. It
is a high-end, fully customizable, powerful software system with
complete database functionality.  It is suitable for many applications
besides shopping carts, though that is its main bent. For example,
you can build a complete database-oriented content display system
using MiniVend.

MiniVend plugs into a system with an SSL (Secure Sockets Layer) server,
allowing encrypted transmission of sensitive customer data. This
capability makes the entry of credit card numbers practical and secure.
In addition, it supports online payment systems and encryption; no
credit card numbers need be stored on the system "en clair".

Many different catalogs can be run from the same MiniVend server,
allowing an ISP to serve many different customers from one or just
a few MiniVend server processes. As many as 2,000 MiniVend catalogs have
been run on one machine from the same server process.

MiniVend is powerful, and correspondingly complex. It can easily handle
catalogs of a million items or more, with excellent performance. It has
completely flexible page display, search, and order entry capability. If
you only have a few items to catalog, MiniVend is probably overkill for
your needs. But if you are willing to spend some up-front learning time,
it can support your simple catalog with unlimited room to grow. To get a
fast start with a simple catalog, start with the simple demo and customize
from there.

=head1 OVERVIEW OF MINIVEND

The following section describes how MiniVend works, and should be read
by anyone creating a MiniVend catalog.

=head2 The Vend Concept

MiniVend is a descendent of Vend, originally developed by Andrew Wilcox in
1995.  Though the original Vend was much simpler than MiniVend in
implementation, the basic concept remains unchanged. Quite simply, MiniVend
maintains its own set of pages, I<outside of regular HTML space>, which contain
special tags that are interpreted by MiniVend.

The tags, which are in C<[square brackets]>, are interpreted by MiniVend and
many different values can be substituted. Some examples are:

=over 4

=item User form input

MiniVend remembers input by a user from form to form, and the value of any
form variable is "remembered" and inserted upon finding a C<[value input_field]>
tag. The C<input_field> is a normal HTML form field.

=item Database contents

MiniVend can have an unlimited number of attached databases, either in one of
its own internal formats or attached via SQL/ODBC.  The contents of a database
can be referenced with tags like C<[data table=products column=name key=334-12]>
or C<[query sql="select * from products where category = 'Clothing'"]>.

=item Session parameters

Things like where the user
originally found your catalog C<[data session referer]>),
domain they are from (C<[data session host]>),
source of hit in a partner program (C<[data session source]>),
the time of their last access (C<[data session time]>),
and many other parameters.

=item Embedded Perl ala ASP

MiniVend has a powerful object system that allows direct access
to Perl and external programs. There is an ASP-like syntax which
can be used, or the traditional MiniVend tag approach can be employed.

=item File contents or program output

You can insert the contents of an outboard file with
C<[file directory/file]> or C<[include directory/file]>, or the output from an
arbitrary program (given proper permission from your administrator!).

=item Searches of files

MiniVend supports different search engines, including I<Glimpse>, or you can
make your own SQL database queries and have MiniVend process the output for you.

=back

There are over 80 different distinct tags supporting hundreds of functions. In
addition, the user can easily implement tags fully as powerful as the standard
tags.

=head2 A typical user session

The user hears about your catalog via a search engine, link from another page,
or click-thru from a banner ad. They access the link, which is a URL pointing
to the MiniVend CGI link program (generically called VLINK or TLINK, more on
that below).  The MiniVend server is already running on your system, and your
catalog has been designed and tested.

The link, which is a regular CGI program, calls the MiniVend server
through a socket.  The MiniVend server sees the path information which
is appended to the URL calling it, and brings up the corresponding page.
The page contains a link to find or order items from your catalog.

The user clicks on the link and MiniVend looks in the I<products> database,
finds the item, and places it in the user's shopping cart. (Each user has
a separate shopping cart, which is attached to their I<session>.)

Once the user decides to purchase, they check out by filling out a form
with their name, address, payment information, etc. In the process they
may make choices about how the product should be shipped, how they will
pay, and provide any other information you may ask for. They then place
(or "submit") the order.

Their payment may be taken at that point via real-time electronic payment
and a soft-goods product downloaded -- or their order information may
be simply sent to you, the store owner, via encrypted email or FAX.
The order is saved to a file or database table as backup, or in the case
of fully-automated systems sent directly to an order entry program or
database link.

All of these operations are fully configurable by you.  The base MiniVend
distribution includes a sample store -- some users have simply customized
the text and images inside, changed the database entries, and opened
their store. You will probably want to fully customize for a distinctive
catalog look and feel.

=head2 How MiniVend Manages Sessions

Normally, each request for a World Wide Web page which comes in to a
server stands on its own. While the server will probably know which
machine a request comes from, it may not know if the next request
comes from the same browser or even from the same user on that
machine.

MiniVend keeps track of who is ordering by one of two means:

=over 4

=item Cookies

MiniVend can issue cookies that contain the user session ID.
If the user returns the cookie, then they can be presented 
pages without accompanying session information.

=item URL re-writing

If the user doesn't want to use cookies for whatever reason, MiniVend
will include in the URL a I<session id>, which is a random piece of text
which is different for each customer browsing the catalog.

=back

MiniVend will automatically do the right thing depending on whether the
user gives back a cookie.

Pages in the catalog served by MiniVend running as a cgi-bin program generate a
special URL for every link. Here is an example of such a URL:

    http://www.minivend.com/cgi-bin/simple/browse1?id=WehUkATn&pc=122&ar=99-102

An explanation of each part:

=over 4

=item www.minivend.com

Internet address of the server hosting the MiniVend catalog.

=item cgi-bin

Informs server that the requested page will be generated by a
program. This can be done any way your HTTP server will recognize.

=item simple

Name of the program to run -- this is MiniVend's VLINK or TLINK

=item browse

Page of the catalog to display

=item id=WehUkATn

The session ID

=item &

Separates session ID from other other parameters (normal HTTP)

=item mv_arg=99-102

An argument which can be used by MiniVend to select page display options.

=item &

Separates argument from other parameters (normal HTTP)

=item mv_pc=122

A unique integer (or source code, if it contains a letter) which prevents
caching servers from caching the URL.

=back

=head2 Page Delivery

MiniVend pages are written in regular HTML with extensions to
support catalog ordering. MiniVend extensions look like:

    <A HREF="[href specials]">See our specials!</A>

Pages are delivered through the following steps:

=over 4

=item *

The HTTPD server (Apache, Netscape, or NCSA are examples of HTTP
servers) receives a request for a MiniVend page.

=item *

The server is already running as a daemon, and the request calls a small
C program (source is vlink.c or tlink.c) that is
I<named according to which catalog is being called.> This program
communicates with the MiniVend program via
a UNIX- or INET-domain socket.

=item *

MiniVend reads the source page from the MiniVend pages directory, and
interprets the MiniVend tags in the file. If the page doesn't exist,
and corresponds to a part number in the database, it is built "on the
fly" using a template page. In the process, it may read or modify any
number of database tables. If the user's browser doesn't accept cookies,
then any links generated on the page will contain the I<session ID>, which
is needed to ensure the user's session is retained.

=item *

The page, which is now entirely in regular HTML, is delivered to
the HTTP server, which returns it to the browser.

=back

=head1 DISTRIBUTION AND SUPPORT INFORMATION

MiniVend is normally free of charge, and is distributed under the GNU
general public license.  This means that individuals and organizations,
both commercial and non-commercial, may use MiniVend without charge. If
you modify and redistribute it, there are certain obligations you
must fulfill. See the file B<Copying> which came with your MiniVend
distribution for the full license.

MiniVend is not guaranteed to be supported other than by making
full source code available. If it breaks you get to keep both pieces.
However, the author is always looking to improve MiniVend and sometimes
answers questions.  The more concise and better-researched your question,
the more likely it is to get an answer. No tutorials will be provided,
though. You have to learn all of this stuff on your own, or use the
MiniVend mail list and hope that someone will help you.

=head2 Where to Download MiniVend

The latest stable MiniVend is available from:

    http://www.minivend.com/minivend/download.html

=head2 Perl

You will need Perl version 5.005 or higher to run MiniVend 4.0. Many
sites are still running lower Perl versions.
You can download the latest Perl 5 from any CPAN (Comprehensive Perl
Archive Network) site. See

    http://www.perl.com/CPAN/

Windows users will need to obtain the ActiveState Perl build
5xx. No earlier version will work -- for example, the old version
on the NT Resource kit will definitely not work.

In addition, on systems that do not have GDBM or DB_File installed,
large catalogs will use large amounts of memory if the databases must
all reside there. If you use SQL, this is not a problem.

=head1 QUICK START

Obtain, decompress and untar the distribution:

    gzip -dc minivend-4.xx.tar.gz | tar xvf -

NOTE FOR WINDOWS: Windows users need to unzip the file WinZip or a similar
program or obtain the self-extracting executable.

Before installing, check the site where you obtained MiniVend for any
patches that might have been issued since the release.

Change to the created directory, something like:

    cd minivend-4.xx

On UNIX, you perform the normal Perl installation method:

    perl Makefile.PL
    make
    make test && make install

Replace the C<perl> with the proper path to your Perl 5.005
or higher binary.

You will be asked for the directory where you want to install MiniVend --
any directory will do. The defaults are:

    Installed as root: /usr/local/minivend
    Installed as user: ~/mvend

NOTE: ~ is shorthand for your home directory.

You must of course have write permission there. This
directory is referred to later in the documentation as B<VendRoot>
or the B<MiniVend software directory>.

To install the link program, you will eventually need to have write
permission on your CGI and HTML directories. The definition of these
are typically part of your HTTP server configuration; In Apache, the
CGI directory is set by B<ScriptAlias>; the HTML directory is set
by B<DocumentRoot>.

The process should be self-explanatory. If you have trouble answering
the questions asked, B<look closely> at the examples provided. If you
still have trouble, you will need to find a tutorial about the World
Wide Web -- the WWW FAQ at www.boutell.com would be a good place to look.

If you discover any problems, refer to the section I<If something goes wrong>.
Otherwise, MiniVend should be installed at the completion of the
script. It is strongly suggested that you install the demo catalogs as
a starting point for your own catalog -- in fact you will not be able
to do anything useful with MiniVend until you have created a catalog.

You will want separate directories to hold the catalog pages and
databases. The C<makecat> program supplied with MiniVend will make those
for you.

B<IMPORTANT NOTE:> One point that is to be emphasized --
I<only your base html pages go in the document space of your http server.>
Any pages with MiniVend elements/tags go in the directory set by the
I<PageDir> directive (the default is C<~/catalogs/catalog_name/pages>).
For the demos supplied with MiniVend, this means that only one or a few pages
will be copied to your HTML directory, with the remainder of the pages
staying in the directory defined as I<PageDir>.

If you are on an ISP where B<all> of your files are in HTML document
space, you should disable all access to your MiniVend catalog directory
with the proper HTTP access restrictions. Normally that is creating
a .htaccess file like this:

  <Limit GET POST>
  order allow,deny
  deny from all
  </Limit>

If you are unable to do this, it is recommended that you do not run
MiniVend. If you can set file permissions such that files will not be
served, it may be OK, but security will be a problem. Please be careful
with your customers' personal information.

=head2 The Catalog Directory

MiniVend pages are NOT in normal HTML space. They are contained in the
I<catalog directory>. Each individual catalog must have its own base
directory. The catalog directory has this structure by default:

=over 4

=item catalog.cfg

File containing configuration directives for this
catalog. (Subcatalogs have differing information in a file
named for the subcatalog.)

=item config

Directory that will be read when directives are set with the
<filename notation. For example, the file C<config/static.pages>
will be read when the directive

 StaticPage  <static.pages

is encountered in the catalog.cfg file.

This directory also contains template information used with
the makecat program.

=item error.log

File which contains catalog-specific errors.
I<Check this file when something doesn't work right.>
It is also where any syntax errors in embedded Perl code will be shown.

=item etc

Directory normally used for tracking files, order profiles, and other
configuration and log information.

=item pages

Directory that contains the pages of your catalog. This
can be considered to be the "document root" of the catalog.  Pages
contained therein are called with the path information after the
script name -- i.e. C</cgi-bin/simple/products/gold> will call the
page in the file C<pages/products/gold.html>.

=item products

Directory containing database source files, including the special MiniVend
databases shipping.asc, pricing.asc (and other shipping
database files)

=item session

Directory which contains session files.

=item tmp

The temporary or scratch directory for various uses like retired
ID numbers, search paging files, sort tests, import temporary files,
etc.  This is the default set by I<ScratchDir>; if you wish you can
redefine it to be located on another partition.

=back

=head2 makecat -- set up a catalog from a template

The supplied C<makecat> script, which is in the MiniVend program directory
C<bin>, is designed to set up a catalog based on your server configuration.
It interrogates the user for parameters like the directories to use, URL to
base the catalog in, HTTP server definitions, and file ownership. It is
self-documenting in that it asks verbose questions and gives relevant
examples.

The C<makecat> script needs a template catalog to operate on. The
I<simple> and I<basic> demo templates are distributed with
MiniVend. You can also look for older demo catalogs (mostly for ideas)
at C<ftp.minivend.com>.

B<IMPORTANT NOTE:> You only make a catalog once. All further configuration
is done by editing the files within the I<catalog directory>.

A catalog template contains an image of a configured catalog. The best
way to see what the makecat program does is to configure the simple
demo and then run a recursive C<diff> on the template and configured
catalog directories:

  diff -r mvend/simple catalogs/simple

NOTE: C<diff> is usually only available on UNIX.

You will see that the files are mostly the same, except that certain
macro strings have been replaced with the answers you gave to the script.
For example, if you answered C<www.mydomain.com> at the prompt
for server name, then you would see this difference in the catalog.cfg file:

    # template
    Variable SERVER_NAME  &#32;_MVC_SERVERNAME__

    # configured catalog
    Variable SERVER_NAME  www.mydomain.com

The macro string &#32;_MVC_SERVERNAME__ was substituted with the answer to
the question about server name.  In the same way, other variables are
substituted, and include (at least):

    MVC_BASEDIR      MVC_IMAGEDIR
    MVC_CATROOT      MVC_IMAGEURL
    MVC_CATUSER      MVC_MAILORDERTO
    MVC_CGIBASE      MVC_MINIVENDGROUP
    MVC_CGIDIR       MVC_MINIVENDUSER
    MVC_CGIURL       MVC_SAMPLEHTML
    MVC_DEMOTYPE     MVC_SAMPLEURL
    MVC_DOCUMENTROOT MVC_VENDROOT
    MVC_ENCRYPTOR

(Not all of these are present in the simple or sample templates, and
quite a few more may be defined.)  In fact, any environment variable that
is set and begins with MVC_ will be substituted for by the C<makecat>
script. So if you wanted to set up a configurable parameter to customize
the COMPANY variable in catalog.cfg, you could run a pre-qualifying
script that set the environment variable MVC_COMPANY and then place in
the catalog.cfg file:

    Variable   COMPANY   &#32;_MVC_COMPANY__

All files within a template directory are substituted for macros,
not just the catalog.cfg file. There are two special directories
named C<html> and C<images>. These will be recursively copied to
the directories defined as SampleHTML and ImageDir.

B<IMPORTANT NOTE:> The template directory is located in the MiniVend
software directory, i.e. where C<minivend.cfg> resides. You normally do
not edit files in the template directory.  If you want to try creating
your own template, it is recommended that you name it something besides
simple and copy the C<simple> demo directory to it as a starting point.
Templates are normally placed in the MiniVend base directory, but can
be located anywhere -- the script will prompt you for location if it
cannot find a template.

In addition to the standard parameters prompted for by MiniVend, and
the standard catalog creation procedure, you may define four other
files in the C<config> directory of the template:

    additional_fields  -- file with more parameters for macro substitution
    additional_help    -- extended description for the additional_fields
    precopy_commands   -- commands passed to the system prior to catalog copy
    postcopy_commands  -- commands passed to the system after catalog copy

All files are paragraph-based; in other words, a blank line (with no spaces)
terminates the individual setting.

The I<additional_fields> file contains:

    PARAM
    The prompt. Set PARAM to?
    The default value of PARAM

This would cause a question during makecat:

    The prompt. Set PARAM to?.....[The default value of PARAM]

If the I<additional_help> file is present, you can give additional
instructions for PARAM.

    PARAM
    These are additional instructions for PARAM, and they
    may span multiple lines up to the first blank line.

The prompt would now be:

    These are additional instructions for PARAM, and they
    may span multiple lines up to the first blank line.

    The prompt. Set PARAM to?.....[The default value of PARAM]

If the file I<config/precopy_commands> exists, it will be read as
a command followed by the prompt/help value.

    mysqladmin create &#32;_MVC_CATALOGNAME__
    We need to create an SQL database for your Minivend
    database tables.

This will cause the prompt:

    We need to create an SQL database for your Minivend
    database tables.
     
    Run command "mysqladmin create simple"?

If the response is "y" or "yes", then the command will be run
by passing it through the Perl system() function. As with any
of the additional configuration files, MVC_PARAM macro substitution
is done on the command and help. Obviously you must have proper
permissions for the command.

The file I<config/postcopy_commands> is exactly the same as I<precopy_commands>
except you are prompted I<after> the catalog files are copied and
macro substitution is performed on all files.

=head2 Setting up multiple catalogs

MiniVend has multiple catalog capability, and therefore breaks the
configuration files into two pieces. One is global (C<minivend.cfg>)
and affects every catalog running under it. The other (C<catalog.cfg>)
is specific to an individual catalog, and has no effect on other catalogs.

The global C<minivend.cfg> file is located in the main MiniVend directory,
and has only a few server-wide configuration parameters. The most
important is the I<Catalog> directive, which defines the catalogs will
be created at server startup. The I<Catalog> directive is often set up
by the C<makecat> program, which can be used to configure a catalog.

Here is an example I<Catalog> directive:

 Catalog simple /home/catalogs/simple /cgi-bin/simple /secure-bin/simple

=over 4

=item simple

The I<catalog identifier>, used as the name of the catalog on command
lines. In the supplied demo configuration this would be C<simple>.
The identifier can contain characters from the set [B<A-Za-z0-9_>].

=item /home/catalogs/simple

The directory where the catalog.cfg file may be found, and usually
the directory where pages and databases are kept.

=item /cgi-bin/simple  /secure-bin/simple

The script names which, when containing a MiniVend link program, will
cause that catalog to be called. At least one must be supplied,
and the same name may not be used for more than one catalog
unless the I<FullURL> directive is specified, in which case
the parameter may be specified as www.yourcompany.com/cgi-bin/simple
and www.theirs.com/cgi-bin/simple may call a different catalog.

=back

There may also be I<SubCatalog> directives:

  SubCatalog easy simple /home/catalogs/simple /cgi-bin/easy

=over 4

=item easy

The name of the subcatalog, which also controls the name of
the subcatalog configuration file -- in this case C<easy.cfg>.

=item simple

The name of the base configuration, which will be the basis
for the catalog. Parameters in the easy.cfg file that are
different will override those in the catalog.cfg file for
the base configuration.

=back

The remaining parameters are as in the I<Catalog> directive.

Additional minivend.cfg parameters set up administrative parameters
that are catalog wide -- see I<Server Configuration File> for details
on each of these.

Each catalog can be completely independent, with different databases --
or catalogs can share any or all pages, databases, and session files. This
means that several catalogs can share the same information, allowing
"virtual malls".

=head2 If something goes wrong

MiniVend is a complex program, and needs the services of other complex
programs (i.e. web servers and relational databases) to work. When there
is a problem, it is not always MiniVend. It may have to do with Perl or
your HTTP server setup. In fact, in the over four years of MiniVend's
existence many more basic installation problems have to do with those
than with MiniVend itself.

If you get a message about not being able to find libraries, or if you
get a core dump or segment fault message, it is always an improperly built
or configured Perl and has nothing to do with MiniVend. Contact your
system administrator or install a new Perl yourself.

The C<makecat> program is intended to be used to create the
starting point for the catalog. If you don't get the demo to work
the first time, keep trying.  If you still can't get the demo
to work, try running in INET mode.  Finally, see the MiniVend FAQ at:

    http://www.minivend.com/minivend/faq/

Check the two error log files -- C<error.log> in the MiniVend
home directory (where minivend.cfg resides) and C<error.log> in the
catalog directory (where catalog.cfg resides; there can be many of
these). Many problems can be diagnosed quickly if these error logs
are consulted.

Check the README file, the FAQ, and mail list archive at the official
MiniVend web site for information:

    http://www.minivend.com/minivend/

You may subscribe to the MiniVend users mail list by sending the
B<message text> C<subscribe minivend-users> to:

    majordomo@minivend.com

Double check that you have the following things:

=over 4

=item 1

Using UNIX sockets: Check that the C<vlink> program is SUID, or you have
made appropriate changes in the I<SocketPerms> directive.  Unless the
files are world-writable, the vlink program and the MiniVend server
must run as the same user ID! If you are running CGI-WRAP or SUEXEC,
then you will find that the C<vlink> program must I<not> be SUID.

If you have trouble with the vlink program (named C<simple> in the demo
configuration), try re-running C<makecat> and using INET mode instead.  (Or you
can copy the C<tlink> INET mode link program over C<vlink>). This should work
unchanged for many systems, but if you are on an ISP or have a non-standard
network configuration you may have to make some changes to I<minivend.cfg>.
For C<tlink> to work you must have the proper host name(s) configured into the
I<TcpHost> directive in I<minivend.cfg>. The program selects port 7786 by
default (the ASCII codes for "M" and "V") -- if you decide to use another port,
you must set the same number in both the tlink program (by running
C<compile_link>) and the C<minivend.cfg> file.

The C<tlink> program does B<not> need to be SUID.

=item 2

That you have proper file permissions.

B<IMPORTANT NOTE:> The MiniVend server should not run as the user
C<nobody>!

The program files can be owned by anyone, but any databases, ASCII
database source files, error logs, and the directory that holds them
must be B<writable> by the proper user ID, that is the one that is
executing the minivend program.  The best way to operate in
multi-user, multi-catalog setups is to create a special C<minivend>
user, then put that user in the group that each catalog user is in. If
you can define a group for each individual user, that provides the best
security. Then all associated files can be in 660 or 770 mode, and you
should have no problems with permissions, and no problems with security.

=item 3

The C<vlink> program is being executed on a machine that has the
socket file C<etc/socket> on a directly attached disk. UNIX-domain
sockets will not work on NFS-mounted filesystems!  That means the
server C<minivend> and the CGI program C<vlink> must be executing
on the same machine.

The C<tlink> program does not have this problem, but it must have the
proper host name(s) and TCP ports set in the I<TcpHost> and I<TcpPort>
directives in C<minivend.cfg>. Also, you should be careful of security
if sensitive information like customer credit card numbers is being
placed on a network wire.

=back

MiniVend is an ambitious and complex program, and is not presented as
being I<easy to use>, I<easy to install>, or I<bug-free>. The
configuration scripts were done to try and make a very painful process
only slightly painful. Some people install in one pass. Others never
make it, especially when they are running on an ISP with a restrictive
setup. Determined and thoughtful users almost always make MiniVend work.

=head1 SETTING UP YOUR CATALOG

MiniVend uses its own tags to implement catalog functions -- they
are similar to normal HTML, but are in C<[square brackets]>. They
will be referred to as either I<tags> or I<elements> in this document.

In order to set up a custom catalog, there are a number of steps.

You will need to become familiar with the MiniVend tags and
directives to make your own catalog. The demo catalogs are a good
starting point, but are not a finished product.

=head2  Start with a database

The first thing you must do is develop your product database. This might
contain all of the information used to display pages about your
products -- or just the product code (SKU), short description, and
price. At the minimum, those three fields are required.

Some other things you might put in:

=over 4

=item image

A database field giving the name of an image file to display the
product. Alternatively, you can keep images in files that are named
for the product code -- then display them if they exist (use the
C<[if file file.gif] TAG [/if]> construct).

=item nontaxable

This field should be present if you have items that do not have
sales tax calculated for them.

=item size

A comma-separated string containing product size information,
for example:

  Small, Medium, Large, XL

You can also define any other attribute information in a database
field.

=item weight

The shipping weight of an item is useful for UPS lookup or
other weight-based shipping calculations.

=item category

If you wish to do one-click category searches to build product
directories, you might use this field to select on.

=item related

Items related to the item in the record. Using MiniVend's C<[loop item]>
tag, you could build product subclasses and accessory sets.

=back

By default, all database source files are located in the in the
C<products> subdirectory of the catalog directory. The main products
database we discussed above is in the file C<products/products.txt>
in the supplied demo catalogs. If you use one of the internal database
methods, then any changes that are made to the ASCII source file will
be reflected in the database on the next access by a user. If you have
a very large database, this may not be what you want -- it can take some
time to build a large database. If you have less than a thousand records
like the ones shown above in your products database, you normally need
not worry -- updates will be almost instantaneous. (See I<NoImport>
if you wish to stop auto updating.)

=head2  Features to use

MiniVend's demo catalog shows many of MiniVend's features -- list
and category building via controlled search or query, random selection
of feature items, templating with active elements, etc. But each feature
has its cost in complexity and performance. The more you ask the computer
to do for every page it displays, the less performance you can expect
under high load. 

Some things that have been found to be popular:

=over 4

=item Category searches

If you have only a small number of products, hard coded pages are just
fine, though you would be surprised how much of a maintenance headache
they are compared to database definitions. If you have any number of
products though, you will want to categorize them so that customers
can easily find them. Pick a field in your database, perhaps named
C<category>, and classify them for search by MiniVend.

=item Images

You can easily place a thumbnail image (perhaps with a link to
a blowup) only for those items that have them. The best way to
do this is with an image field in the database; if you do file
tests for every image in a large list it can degrade system performance.

=item Related items

On a product's individual display page, you can embed searches
of similar products with the C<[query ...]> or C<[loop ...]> tags.
If you develop customer data, you can even search a past order database
and present products that previous buyers of that product have selected.

=item Reviews/Testimonials

You can key the placement on the existence of a file in
a certain directory. This is very reasonable to do when a 
user is viewing a single product.

=back

=head2 Use the demo catalogs

The demo catalogs supplied with MiniVend are there to give you a starting
point for your own catalog. Play with them, change them, and rename them --
add your own icons, change the pages, change the catalog.cfg file, etc.
Each catalog is independent, so you cannot do any harm. 8-)

=head2 Tree design

Determine how users will enter and exit your catalog. There are quite
complex and intelligent conditional schemes possible, especially if you
use the I<Cookies> capability, but simplicity should be the aim. No one
can buy from you if they cannot navigate their way around!

The most important thing to remember is that if you are supporting browsers
which might not accept cookies, you must never send the user to a page that
is not served by MiniVend. If you do, they will lose their session (and items
in their shopping cart). MiniVend will help in this regard, but you must
use its C<area> and C<page> tags.

If you are using frames, you must be careful to source all frame panes
containing MiniVend links from an initial page served by MiniVend. If you do not,
then you may find the user has multiple session IDs depending on which
frame the link came from.

=head2 The Essentials

The rest of this section describes the rest of the things you need to
know to make the most basic of MiniVend catalogs, one which displays
pages and uses the demo shopping basket and checkout sequence. If you
want to add custom features, like special shipping charges and
sales tax, you will need to go much further. But this will get you
started.

All of the mentioned features (and more) are demonstrated in the
C<simple> demo catalog.

=head2 The Demo Catalog

Sample catalog pages are in the directory simple/. If you would like to
use them as a starting point for your own catalog, you can either have
the configure script install the demo for you, or you can copy the files
into the MiniVend directory and your HTML directory.

To install the demo and name it C<simple>:

 bin/makecat simple

To install the demo and name it C<foo>:

 bin/makecat foo

Answer the prompts supplied by the program. Note that there are
two types of paths asked for, URL paths like the /cgi-bin inside
http://www.machine.com/cgi-bin/simple, and file paths that are complete
fully-qualified file path names like C</home/user/catalogs/simple>
and C</home/httpd/cgi-bin>.

=head2 A bit about "simple"

The C<simple> catalog really isn't. It is a fairly full-featured demonstration
of MiniVend capabilities, though not nearly all of its features are used there.

It uses the C<Variable> feature extensively to simplify hand page editing.
Basically speaking, a C<Variable> is a I<define> that allows you to substitute
text for a simple __VARIABLE__ string in your page.

For example, in the simple demo, this is a complete page with the standard
layout:

    __LOGOBAR__
    __MENUBAR__
    __LEFTSIDE__
 
        This is your content.
 
    __RIGHTSIDE__
    __MENUBOTTOM__
    __COPYRIGHT__

The way this looks on the page, roughly, is:

    +--------------------------------------------------------+
    |   __LOGOBAR__                                          |
    |--------------------------------------------------------+
    |   __MENUBAR__                                          |
    |--------------+-------------------------+---------------+
    |              |                         |               |
    | __LEFTSIDE__ |   This is your content  | __RIGHTSIDE__ |
    |              |                         |               |
    |              |                         |               |
    +--------------+-------------------------+---------------+
    |   __MENUBOTTOM__                                       |
    |--------------------------------------------------------+
    |   __COPYRIGHT__                                        |
    +--------------------------------------------------------+

The __RIGHTSIDE__ is simply defined to be empty, closing table
HTML in most cases, so it really is this in most pages:

    +--------------------------------------------------------+
    |   __LOGOBAR__                                          |
    |--------------------------------------------------------+
    |   __MENUBAR__                                          |
    |--------------+-----------------------------------------+
    |              |                                         |
    | __LEFTSIDE__ |   This is your content                  |
    |              |                                         |
    |              |                                         |
    +--------------+-----------------------------------------+
    |   __MENUBOTTOM__                                       |
    |--------------------------------------------------------+
    |   __COPYRIGHT__                                        |
    +--------------------------------------------------------+

The variables are defined in C<catalog.cfg>, and come from files
in the config directory. There are conditional templates with some
different color schemes.

Once you get used to using this method, it can be quite convenient.
But you may certainly choose to use your own method, perhaps one based
on an HTML editor. MiniVend can coexist with some editors; ones that
have a <NOTOUCH> </NOTOUCH> or <NOEDIT> </NOEDIT> pair to define areas
that should not be "masterminded" by the editor are best.

Bear in mind that this templating method and database layout of the simple
catalog is a loose suggestion on how your catalog might be organized. Many
users decide to do their own database structure for product display. What
you can do is more limited by your imagination than anything.

Yet most users stick with the userdb and checkout strategies employed
in the catalog. This is because they have been developed over a period of
years and work. Still, these can be customized; you can make it operate
like Amazon or another site if you wish.

As stated before, MiniVend catalogs are all about the database. The
C<simple> demo has several tables:

=over 4

=item area

A table designed to implement a dynamic navigation bar. It is read and
parsed to display the areas C<Galleries>, C<Other Stuff>, and C<Links>
as distributed.  Simply by changing the contents of the C<area> table
you can change the left side navigation bar. The value of the field
C<selector> is used to search the C<cat> table to find which categories
belong in each area.

=item cat

The product structure of simple follows a single-level categorization. Categories
are listed in the C<cat> database table. The C<cat> table is designed to allow
you to display meta-information about each category when it is displayed. The
fields are:

=over 4

=item code

The category code. Must be unique.

=item area

The area this category should be displayed in.

=item selector

The selector that is used to scan the C<products> table for products in the
category.

=item name

A label to display, so that you don't have to live with the C<code> or C<selector>.

=item banner_text

Text to display in a banner or heading.

=item subs

Placeholder in the demo; intended to allow selection of certain subcategories
within the category.

=item sort

The sort order if you don't want alphanumeric on C<name>.

=item url

A URL to link to instead of the default search in the products database.

=back

=item country

A list of countries; used to build select boxes and select shipping modes based
on countries.

=item inventory

Two fields; code and quantity. Minivend's demo will decrement the quantity
based on the sale. That is easy; your method of keeping inventory current
is the hard part. It might make sense to add a field with the usual shipping
time of the product; something like "ships within 24 hours", or "back ordered;
allow 2-4 weeks for delivery".

=item pricing

A database that works in conjunction with the C<CommonAdjust> directive
to allow quantity pricing, either for one product or for a group of products
(sometimes known as mix-and-match). The fields C<q2,q5,q10,etc.> are for
the quantity levels; the C<price_group> field selects the mix-and-match
category for the product.

=item products

The main product table. This is where you store your product information.
You can add, delete, or ignore fields as you choose. Field by field:

=over 4

=item sku

The unique identifier for the product. Though theoretically it can contain
most any character, you should use only characters of the class 
C<A-Z a-z 0-9 _ -> if you want to be easily compatible with SQL databases
and URL encoding. Other characters can cause problems; a slash (C</>) can interfere
with URLs and filenames; a colon (C<:>) can interfere with database representations
(or file names on some operating systems);
and there are other possible compatibility situations with other characters.

=item description

A B<short> description for the product, used for displaying in the shopping
cart, receipt, and order report. Keep it short.

=item title

This is somewhat catalog-specific since it applies to art. However, it would
also apply to books, CDs, or other items.

=item artist

This is somewhat catalog-specific since it applies to art. However, it would
also apply to CDs, or some other items. You can change it to anything if you
remember to change the field names in your catalog pages to match; to do
so look for all occurences of the string "artist" in the C<pages>, C<special_pages>,
and C<etc> directories.

=item comment

A long description of the product. If you use the Minivend internal database, the
field size is unlimited; if you use another type of database then you will be
dependent on the field type you select. 

=item display

Catalog-specific to show the museum the art work is displayed at. You can use
or delete this field as you wish.

=item image

The image filename for the product. Many people add a C<thumb> field to contain
the name of a small image for search list display.

=item price

The quantity-one price of the product.

=item category

The category used for selection in lists. It is possible to place a product
in more than one category, though you will have some display and banner heading
decisions to make based upon this. You can use embedded Perl or other methods
to deal with this.

=item nontaxable

If set to C<1>, the product is not taxable and its price will not be used
in a salestax calculation.

=item weight

A numeric value of the weight, used for determining shipping costs.
Normally in pounds, but can be anything your shipping routines will handle.

=item size

A comma-separated list of options as used in C<Accessories>. See the 
C<flypage.html> and C<ord/basket.html> pages for examples of how it
is used.

=item color

A comma-separated list of options as used in C<Accessories>. See the 
C<flypage.html> and C<ord/basket.html> pages for examples of how it
is used.

=item related

Used in the demo to display "upsells", meaning opportunities to purchase
an additional item when you purchase this one. Contains a list of SKUs
to be offered.

=item featured

As used in this demo, the only value that means anything is C<front>. If
that is contained in the field, this product will possibly be featured
on the front page via random selection. See the C<index.html> page for
how it integrates with C<[loop search=something random=3]>.

=back

=item orderline

Every line item that is actually ordered is detailed in this table. See the 
page C<query/check_orders.html> for how it might be used; see C<etc/report>
for how it is added to.

=item transactions

Each individual customer order is detailed in this table. See the 
page C<query/check_orders.html> for how it might be used; see C<etc/report>
for how it is added to.

=item userdb

The user database used for maintaining customer address information, account
information, preferences, and more. See C<UserDB>.


=back


=head2 MML -- MiniVend Markup Language

MiniVend functions are accessed via MML, the MiniVend Markup Language.
The pages in the catalog are in HTML, but use MML tags to provide access
to MiniVend's funtions. MML tags use C<[square brackets]> instead
of angular brackets. We will usually call them B<MiniVend tags> or
just B<tags>. They are similar to HTML in syntax, i.e. there are both
<I>standalone</I> and <I>container</I> tags. A standalone tag has no
ending element, i.e.:

    [value name]

This tag will insert the user's name, providing they have given it
to you in a form. A container tag has both a beginning and an ending
element, as in:

    [if value name]
    You have a name.
    [/if]

These tags perform various display and modification operations for
the user session. There are over 80 standard predefined tags, and
the C<UserTag> facility allows you to create tags fully as powerful
that perform your own functions.

Don't expect to understand all of the tags to begin with, there are
many. As an introduction, a few of the the tag names and
their general function are:

 [accessories]     Access product accessory functions
 [area]            Insert a re-written MiniVend URL
 [checked]         Conditionally check an HTML check/radio box
 [currency]        Formats a number like currency for current locale
   [/currency]    
 [data]            Access a database or user session element
 [error]           Check/display form processing errors
 [file]            Insert the contents of a file
 [flag]            Set a minivend flag
 [fly-list]        Show an item "on-the-fly" in an arbitrary page
   [/fly-list]      
 [html_table]      create an HTML table from a query or list
 [include]         Include a file with complete MiniVend interpretation
 [item-list]       Iterate over a shopping cart
   [/item-list]  
 [label]           Set a label for goto
 [loop]            Iterate over an arbitrary list
   [/loop]       
 [mvasp]           ASP-style Perl programming area
 [no-match]        Define area of region results displayed when no match
   [/no-match]   
 [nitems]          Show number of items for a shopping cart
 [order]           Create HTML link to order an item
 [page]            Create A HREF with re-written URL to call MiniVend page
 [perl]            Embed output of arbitrary Perl in the page
   [/perl]       
 [price]           Show price of an item
 [process]         Create URL for MiniVend form processing
 [query]           Perform any of several types of SQL query
   [/query]       
 [record]          Set a database record
 [region]          Define an area of the page as a search list/query
 [row]             Used with [col] -- rudimentary text tables for order reports
   [/row]        
 [salestax]        Show amount of salestax for shopping cart
 [scratch]         Access a scratch variable
 [search-list]     Display output of a MiniVend search
   [/search-list] 
 [selected]        Conditional selection of drop-down <SELECT ...>
 [set]             Set a scratch variable
   [/set]        
 [sort]            Set sort order for iterating lists
   [/sort]       
 [subtotal]        Calculate subtotal without tax or shipping
 [total-cost]      Calculate order total with tax, handling, and shipping
 [userdb]          Access user database functions
 [value]           Display form value
 [value_extended]  Display form array values or do file upload functions

A complete list of tags and all tag syntax is shown in the I<TAG REFERENCE>.

=head2 HTML Hypertext links

You will normally not want to use regular hypertext links in MiniVend
pages. Such links will not include the session id, which means that if
the customer follows an external link back to the catalog the list of
products ordered so far will have been lost.

So instead of:

    <A HREF="/cgi-bin/mv/shirts">Shirts</A>

You would use:

    <A HREF="[area shirts]">Shirts</A>

=head2 Images

Inline images are placed in MiniVend pages in the normal fashion with
<IMG SRC="URL">. But since MiniVend pages are served by a CGI program you
cannot use relative links.  MiniVend has the capability of defining an
image directory (with the I<ImageDir> and I<ImageDirSecure> directives)
that automatically adjusts your image path to a set base directory.

=head2 Cookies

All you need to do to have users with cookie-capable browsers retain
session context is enable the I<Cookies> directive. You can then
intermix standard HREF and MiniVend page links without fear of losing
the shopping basket. Cookie capability is also required to use search
caching, page caching, and statically generated pages. If the browser
does not support cookies, the cache will be ignored.

If you plan to use more than one host name I<within the same domain> for
naming purposes (perhaps a secure server and non-secure server) then
you can set the domain with the I<CookieDomain> directive. This must
contain at least two periods (.) as per the cookie specification, and
you cannot set a domain that your server is not located within.

=head2 Basic MiniVend Tags

B<NOTE:> In the descriptions, parameters marked with an asterisk* are
optional.

When a tag is separated by an underscore, as in I<item_list>, a dash
is just as appropriate (i.e. C<item-list>). They are interchangeable,
except that the ending tag and beginning tag should match (don't
use C<[item-list] list [/item_list]>).

B<*> I<indicates an optional argument>

=head2 Inserting hyperlinks

named: [area href="dir/page" secure=1* arg="argument"* form="form string"*]

positional: [area pg arg*]

Produces the URL to call a MiniVend page, without the surrounding
A HREF notation. This can be used to get control of your HREF items,
perhaps to place an ALT string or a Javascript construct.  It was
originally named C<area> because it also can be used in a client-side
image map.

   <A HREF="[area catalog]" ALT="Main catalog page">

The optional I<arg> is used just as in the I<page> tag.

The optional C<form> argument allows you to encode a form in the link.

        <A HREF="[area form="
                mv_order_item=99-102
                mv_order_size=L
                mv_order_quantity=1
                mv_separate_items=1
                mv_todo=refresh"
        ]"> Order t-shirt in Large size </A>

See above for more information.

=over 4

=item [/page], [/order]

Expands into </a>. Used with the page element, such as:

  [page shirts]Our shirt collection[/page].

TIP: A small efficiency boost in large pages is to just use the </A>
tag.

=item [page ...]

named: [page href="dir/page" arg="argument" secure=1* form="form string"]

positional: [page dir/page arg*] (only two positional parameters)

Insert a hyperlink to the specified catalog page pg. For
example, [page shirts] will expand into <
a href="http://machine.company.com/cgi-bin/vlink/shirts?id=WehUkATn&pc=33">. The
catalog page displayed will come from "shirts.html" in the
pages directory.

If the user has sent a cookie to MiniVend (meaning the B<second> page
they access), and you set the scratch value C<mv_no_session_id> in
their session, the session ID will not be appended to the URL. If you
set the scratch value C<mv_no_count>, then the page count will not
be appended; this is not dependent on cookies. So if you put in your
initial page

    [set mv_no_session_id]1[/set]
    [set mv_no_count]1[/set]
    [set mv_add_dot_html]1[/set]

or put in C<catalog.cfg>:

    ScratchDefault  mv_no_session_id  1
    ScratchDefault  mv_no_count       1
    ScratchDefault  mv_add_dot_html   1

no session ID or count will be shown. That makes the URL shown above
be http://machine.company.com/cgi-bin/vlink/shirts.html -- once again,
that is on the I<second> page the user accesses if they are taking
and sending cookies. If the user has a pre-existing MV_SESSION_ID or
MV_USERNAME cookie from a prior session, the effect will be immediate.

The C<argument> will be passed to MiniVend and placed in the
C<mv_arg> session parameter. This allows programming of a conditional page
display based on where the link came from. The argument is then available
with the tag [data session arg], or the embedded Perl session variable
$Session->{arg}. If you set the catalog configuration option
I<NewEscape>, which is the default, then spaces and some other characters
will be escaped with the %NN HTTP-style notation and unescaped when the
argument is read back into the session.

A bit of magic occurs if MiniVend has built a static plain HTML page
for the target page. Instead of generating a normal MiniVend-parsed
page reference, a static page reference will be inserted if the user
has accepted and sent back a cookie with the session ID.

The optional C<form> argument allows you to encode a form in the link.

        [page form="
                mv_order_item=99-102
                mv_order_size=L
                mv_order_quantity=1
                mv_separate_items=1
                mv_todo=refresh"] Order t-shirt in Large size </A>

The two form values I<mv_session_id> and I<mv_arg> are automatically added
when appropriate. (I<mv_arg> is the C<arg> parameter for the tag.)

If the parameter C<href> is not supplied, I<process> is used, causing
normal MiniVend form processing. If the C<href> points to an http://
link no MiniVend URL processing will be done, but the mv_session_id

This would generate a form that ordered item number 99-102 on
a separate line (C<mv_separate_items> being set), with size C<L>,
in quantity 2. Since the page is not set, you will go to the default
shopping cart page -- equally you could set C<mv_orderpage=yourpage>
to go to C<yourpage>.

All normal MiniVend form caveats apply -- you must have an action,
you must supply a page if you don't want to go to the default,
etc.

You can theoretically submit any form with this, though none of the
included values can have newlines or trailing whitespace. If you want
to do something like that you will have to write a UserTag.

You can also use it for submitting foreign forms if you like; it will not touch
the href if it begins with http:, ftp:, or the like.

=item [/page], [/order]

Expands into </a>. Used with the page element, such as:

  [page shirts]Our shirt collection[/page].

TIP: A small efficiency boost in large pages is to just use the </A>
tag.

=back

=head2 How to order an item

MiniVend can either use a form-based order or a link-based order to
place an item in the shopping cart. The link-based order uses the
special C<[order item-code]> tag:

=over 4

=item [order code cart/page* database*]

named attributes: C<[order code="code" href="cart/page" base="database"]>

Expands into a hypertext link which will include the specified
code in the list of products to order and display the order page. B<code>
should be a product code listed in one of the "products" databases. The
optional argument B<cart/page> selects the shopping cart the item will be
placed in (begin with / to use the default cart C<main>) and the order page
that will display the order. The optional argument B<database> constrains
the order to a particular products file -- if not specified, all databases
defined as products files will be searched in sequence for the item.

Example:

  Order a [order TK112]Toaster[/order] today.

Note that this is the same as:

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

You can change frames for the order with:

  Order a <A HREF="[area order TK112]" TARGET=newframe>Toaster</A> today.

=item [/order]

Expands into </a>. Used with the order element, such as: Buy a
C<[order TK112]>ToasterC<[/order]> today.

=back

To order with a form, you set the form variable C<mv_order_item> to
the item-code/SKU and use the C<refresh> action:

  <FORM ACTION="[process-target]" METHOD=POST>
  <INPUT TYPE=hidden  NAME="mv_todo"        VALUE="refresh">
  <INPUT TYPE=hidden  NAME="mv_order_item"  VALUE="TK112">
 
  Order <INPUT NAME="mv_order_quantity" SIZE=3 VALUE=1> toaster
 
  <INPUT TYPE=submit VALUE="Order!">
  </FORM>

You may batch select whole groups of items:


  <FORM ACTION="[process-target]" METHOD=POST>
  <INPUT TYPE=hidden  NAME="mv_todo"        VALUE="refresh">

  <INPUT TYPE=hidden  NAME="mv_order_item"  VALUE="TK112">
  <INPUT NAME="mv_order_quantity" SIZE=3> Standard Toaster

  <INPUT TYPE=hidden  NAME="mv_order_item"  VALUE="TK200">
  <INPUT NAME="mv_order_quantity" SIZE=3> Super Toaster
  
  <INPUT TYPE=submit VALUE="Order!">
  </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.

You may also specify attributes like size or color at time of order.
Order the T-shirt from the more details page of the C<simple> demo
to see how it is done.

=head2 Accessing form values

When you send a form to Minivend, it reads the values and places
them in the user session. The following form:

    <FORM ACTION="[area index]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_action VALUE=return>
    <INPUT TYPE=hidden NAME=foo VALUE=bar>
    <INPUT TYPE=submit NAME="Make foo=bar">
    </FORM>

will make the C<[value foo]> tag equal to C<bar> on the next page
then send you to the page C<index>(.html).
Same for this link:

    <A HREF="[area  href=index
                    form='
                      foo=bar
                      mv_action=return
                    ']">Make foo=bar</A>


=over 4

=item [value name=field set="new value" filter="filter" hide=1]

positional:  C<[value varname]>

The C<[value ...]> MML tag expands into the current value of the customer/form
input field named by field. If the C<set> value is present, the form variable
value will be set to it and the new value returned. Use this to "uncheck" a
checkbox or set other form variable values to defaults. If HIDE is set, the
value will be set but not returned to the page.

The C<filter="filter1 filter1"> parameter applies any of the standard
MiniVend filter types to the value. See I<Filters>.

When the value is returned, any MiniVend tags present in the value will be
escaped. This prevents users from entering MiniVend tags in form values, which
could be a serious security risk.

=back

=head2 Where do I go from here?

MiniVend is very complicated but very powerful. If you have read and
understood the documentation so far, you have a good start on building a
catalog.  There are many, many, more features than are shown in the demo,
and mastering the ones you need will take time. Thousands of people have
built MiniVend catalogs -- you can too. If you feel it is beyond you,
then we would suggest engaging a competent consultant. Good luck!

=head1 DATABASES

MiniVend, as with most powerful shopping cart programs, is all about
databases.

NOTE: No other database besides MiniVend's internal one is needed.
You may find that keeping your database in an SQL manager makes
it easier to integrate MiniVend with other tools. MiniVend is fully
buzzword-equipped, but if you just want to maintain a spreadsheet with
your product information, you can ignore the references to SQL, DBI,
DBD, and all of those other things and just modify the file C<products.txt>
appropriately.

MiniVend implements the database in GDBM, DB_File, SQL,
or in-memory format. In most cases, these should operate the same when
called by MiniVend's access methods.

=head2 A bit about text source files

MiniVend reads TAB-delimited text files to obtain your data. However,
B<the text files are not the database>. They are the source information
for the database tables; when you change them, essentially you are placing
text which will be I<imported> into the database table.

Note the following directive:

    Database  products  products.txt   TAB

This says that the C<products> table will obtain its source information
from the file C<products.txt>. What is done with it depends on the type
of underlying database you are using. The different types:

=over 4

=item GDBM

The database source file is checked to see if it is newer than the
actual database file, which would be C<products.gdbm>. If it is, then
the database table is re-imported from the file.

You can change this behavior in a couple of ways. If you wish the
file never to be imported unless the C<.gdbm> file disappears,
set the C<NoImport> directive:

    NoImport  products

If you want it only to be imported at catalog start-up time, use
the IMPORT_ONCE modifier:

    Database products IMPORT_ONCE 1

GDBM is the default type if you have GDBM_File Perl module installed
(as it will be on Linux).

=item DB_File

The database source file is checked to see if it is newer than the
actual database file, which would be C<products.db>. If it is, then
the database table is re-imported from the file.

You can change this behavior in the same way as with GDBM_File (above).

DB_File is the default type if you B<do not> have the GDBM_File Perl
module installed. This is typical on FreeBSD.

To explicitly specify DB_File as the type, you can specify it
with a Database directive in C<catalog.cfg>:

    Database  products  DB_FILE   1

=item DBI/SQL

If a file named C<products.sql> is present in the same directory
as C<products.txt> the database table will not be imported from ASCII
source. If there is no C<products.sql>, then the following will occur:

DBI/SQL imports only happen at catalog configuration time.

=over 4

=item 1

MiniVend will connect to the SQL database using the
specified DSN. (DBI parameter meaning "Database Source Name".)

=item 1

The table will be dropped with "DROP TABLE products;". This will
occur without warning!

=item 1

The table will be created. If there are COLUMN_DEF
specifications in C<catalog.cfg>, they will be used; otherwise
the key (first field in the text file by default) will be created
with a C<char(16)> type and all other fields will be created as
C<char(128)>. The table creation statement will be written to
the C<error.log> file.

=item 1

The text source file will be imported into the SQL database.
MiniVend will place the data in as in the columns; you must take
care of data typing yourself. This means that if you put "none"
in a field, and it is defined as a numeric type, the database 
import will not succeed. If it does not, the catalog will not
become active.

=back

=item In-memory

Every time the catalog is configured, the C<products.txt> file is imported
into memory and forms the database. The database is not changed otherwise.

In-memory is the default type if there is no GDBM_File or DB_File Perl
module installed; you can also specify it with:

   Database   products   MEMORY   1

=back

=head2 MiniVend Database Operation

NOTE: In the following descriptions, we will use the following
terms somewhat interchangeably:

=over 4

=item key, code

Either one is a reference to the B<key> for the database. In MiniVend
this is often the product B<code> or B<SKU>, which is the part number
for the product.  Other key values may be used to generate relationships
to other database tables.

It is required that the key be the first column of an ASCII source file
for GDBM, Berkeley DB, or in-memory built-in database formats. It is also
strongly suggested that you keep that practice for SQL databases, since
MiniVend's import, export, and search facilities will work much better
with that practice.

=item field, column

This is a column of the database. One of the columns is always the key --
MiniVend prefers that the key be the first column. Field is an interchangeable
reference.

=item table, database

A table in the database. Because of the evolution of MiniVend from a
single-table database to an access method for an unlimited number of
tables (and databases, for that matter), we will sometimes refer to a
table as a database.  The only time B<database> refers to something
different is when describing that concept as it relates to SQL --
where a database contains a series of tables.  MiniVend cannot create
SQL databases, but given the proper permissions it may drop and create
tables within that database.

=back

If necessary, MiniVend reads the data to place in tables from standard
ASCII-delimited files.  All of these ASCII source files are kept in the
products directory, normally B<products> in the catalog directory (where
catalog.cfg is).

The ASCII files can have ^M (carriage return) characters if desired, but must
have a newline character at the B<end> of the line to work -- Mac users
uploading files must use ASCII mode, not binary mode!

MiniVend's default ASCII delimiter is TAB.

B<IMPORTANT NOTE:> The items must be separated by a B<single> delimiter.
The items are lined up for your reading convenience.

=over 4

=item TAB

Fields separated by ^I characters. No whitespace should be at the
beginning of the line.

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

=item PIPE

Fields separated by C<|> characters. No whitespace should be at the
beginning of the line.

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

=item CSV

Fields enclosed in quotes, separated by commas. No whitespace should be at the
beginning of the line.

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

=back

B<NOTE:> Using the default TAB delimiter is highly recommended if you
are planning on searching the ASCII source file of the database. PIPE
works fairly well, but CSV delimiter schemes cause problems with searching.

B<IMPORTANT NOTE:> Field names are usually case-sensitive. Unless
you are consistent in the names, you will have problems. All lower
or all upper case names are recommended.

MiniVend uses one mandatory database, which is referred to as the products
database. In the supplied demo catalogs, it is called I<products> and
the ASCII source is kept in the file C<products.txt> in the products
directory. This file is also the default file for searching with the
I<THE SEARCH ENGINE>.

MiniVend also has a two of standard but optional databases,
which are in fixed special formats:

=over 4

=item shipping.asc

The database of shipping options if the I<CustomShipping> directive
is in use.  This is a fixed-format database, and must be created as specified.
See I<SHIPPING>.

=item salestax.asc

The database of sales tax information if the C<[salestax]> tag is to
be used. A default is supplied -- caution, these things change!
This is a fixed-format database, and must be created as specified.
See I<Sales Tax>.

=back

=head2 The Product Database

Each product you are selling should be given a product code: A short
code that identifies the product on the ordering page and in the
catalog. You can use any combination of letters, digits, dashes,
periods, hash signs, or underscores for the product code. The
F<products.txt> file is a ASCII-delimited list of all the product codes,
along with an arbitrary number of fields which must contain at least the
fields C<description> and C<price> (or whatever you set the PriceField and
DescriptionField directives to). Any additional information you want in
the catalog can be placed in any arbitrary field.
See I<MiniVend Database Capability>for details on the format.

Field names are case-sensitive. Unless you have fields with the names
"description" and "price" field, you will have to appropriately set the
I<PriceField> and I<DescriptionField> directives to use the C<[item-price]>
and C<[item-description]> tags.

The product code must be the first field in the line, and must be
unique. Product codes can contain the characters B<A-Za-z0-9>, along with
hyphen (C<->), underscore (C<_>), pound sign/hash mark (C<#>), slash (C</>),
and period (C<.>).

The words should be separated by one of the approved delimiting schemes
(TAB, PIPE, or CSV), and are case-sensitive in some cases. If you play
with the case of the "description" or "price" field, you will have to
appropriately set the I<PriceField> and I<DescriptionField> directives.

B<NOTE:> CSV is not recommended as the scheme for the products database.
It is much slower than TAB- or PIPE-delimited, and dramatically
reduces search engine functionality -- no field-specific searches are
possible. Don't use it unless you know B<exactly> what you are doing --
you will be sorry if you do. Using CSV for any small database that
will not be searched is fine.

=over 4

B<IMPORTANT NOTE:> The field names must be on the first line of the
C<products.txt> file. These field names must match I<exactly> the field
names of the C<[item-field]> tags in your catalog pages, or the MiniVend
server will not access them properly. Field names can contain the
characters B<A-Za-z0-9>, along with hyphen (C<->), underscore (C<_>),
pound sign/hash mark (C<#>), slash (C</>), and period (C<.>).

More than one database may be used as a products
database. If the catalog directive I<ProductFiles> is set to a space-separated
list of valid MiniVend database identifiers, those databases will be
searched (in the order specified) for any items that are ordered, or
for product information (as in the C<[price code]> and C<[field code]> tags).

=back

When the database table source file (i.e. products.txt) changes after
import or edit, the DBM database is re-built upon the next user access. No
restart of the server is necessary.

If changing the database on the fly, it is recommended that you
lock the file while it is being modified.

=head2 Multiple Database Tables

MiniVend can manage an unlimited number of arbitrary database
tables.  They use the TAB delimiter by default, 
but several flexible delimiter schemes are available. These are
defined by default:

    Type 1      DEFAULT - uses default TAB delimiter
    Type 2      LINE
                Each field on its own line, a blank line
                separates the record. Watch those carriage
                returns! Also has a special format when CONTINUE
                is set to be NOTES.
    Type 3      %%
                Fields separated by a \n%%\n combination, records by
                \n%%%\n (where \n is a newline). Watch those carriage
                returns!
    Type 4      CSV
    Type 5      PIPE
    Type 6      TAB
    Type 7      reserved
    Type 8      SQL

The databases are specified in I<Database> directives, as:

    Database    arbitrary arbitrary.csv CSV

That specifies a type 4 database, the ASCII version of which is located in the
file arbitrary.csv, and the identifier it will be accessed under in MiniVend is
"Arbitrary".  The DBM file, if any, will be created in the B<same directory> if
the ASCII file is newer, or if the DBM file does not exist.  The files will be
created as arbitrary.db or arbitrary.gdbm, depending on DBM type.

The C<identifier> is case sensitive, and can only contain characters
in the class [B<A-Za-z0-9_>]. Fields are accessed with the
[item_data C<identifier> field] or [data C<identifier> field key] elements.

=head2 MiniVend built-in database support

If you specify one of the first 6 types, the database will automatically
be built in the default MiniVend DB style. You can specify the type with
DB_FILE, GDBM, or MEMORY if you want to vary from that default.
They will coexist just fine with an unlimited number of DBI databases
of different types.

In addition to the database, the session files will be kept in the
default format, and are affected by the actions below.

The order of preference is:

=over 4

=item GDBM

This uses the Perl GDBM_File module to build a GDBM database. You can
see if GDBM is in your perl with the command:

    perl -e 'require GDBM_File and print "I have GDBM.\n"'

Installing GDBM_File requires rebuilding Perl after obtaining the
GNU GDBM package, and is beyond the scope of this forum. Linux will
typically have this by default -- most other operating systems will
need to specifically build this in.

=item DB_File (Berkeley DB)

This uses the DB_File module to build a Berkeley DB (hash) database. You can
see if DB_File is in your perl with the command:

    perl -e 'require DB_File and print "I have Berkeley DB.\n"'

Installing DB_File requires rebuilding Perl after obtaining the
Berkeley DB package, and is beyond the scope of this document. BSDI,
FreeBSD, and Linux will typically have it by default -- most other
operating systems will need to specifically build this in.

If you wish to use DB_File even though you have GDBM_File in your
Perl, you must set the environment variable MINIVEND_DBFILE to
a true (non-zero, non-blank) value:

    # csh or tcsh
    setenv MINIVEND_DBFILE 1

    # sh, bash, or ksh
    MINIVEND_DBFILE=1 ; export MINIVEND_DBFILE

Then re-start the server.

Or you can specify the DB_FILE class in catalog.cfg:

    Database arbitrary  DB_FILE  1

=item In-memory

This uses Perl hashes to store the data directly in memory. Every time
you restart the MiniVend server, it will re-import all in-memory databases
for every catalog.

If you wish to use this despite the presence of GDBM_File or DB_File,
set the environment variable MINIVEND_NODBM as above or specify the
memory type in the Database directive:

    Database arbitrary  MEMORY  1

=back

=head2 Character usage restrictions

To review, database identifiers, field names, and product codes (database keys)
are restricted in the characters they may use. A short table showing
restrictions:

                                       Legal characters
                                       ---------------------
    Database identifiers               A-Z a-z 0-9 _
    Field names                        A-Z a-z 0-9 _
    Database keys (product code/SKU)   A-Z a-z 0-9 _ # - . /
    Database values                    Any (subject to field/record delimiter)

Some SQL databases have reserved words which cannot be used as field
names; MiniVend databases do not have this restriction.

For easy HTML compatibility, is not recommended that you use a / in a
part number if you wish to use the flypage capability; you can still
call it with [page href=flypage arg="S/KU"] if you do happen to use it.

=head2 Database Attributes

Especially in SQL databases, there are certain things that can be set with
additional database attributes.  For text import, the CONTINUE extended
database import attribute allows additional control over the format of
imported text.

NOTE: CONTINUE applies to all types except CSV. (You won't want to
use NOTES unless using type LINE.)

=over 4

=item CONTINUE

One of UNIX, DITTO, LINE, NONE, or NOTES. The default, NONE, is to
simply split the line/record according to the delimiter, with no possible
spanning of records. Setting CONTINUE to UNIX appends the next line to
the current when it encounters a backslash (C<\>) at the end of a record,
just like many Unix commands and shells.

DITTO is invoked when the key field is blank -- it adds the contents of
following fields to the one above, separated by a newline character. This
allows additional text to be added to a field beyond the 255 characters
available with most spreadsheets and flat-file databases.

Example in catalog.cfg:

  Database products products.txt  TAB
  Database products CONTINUE      DITTO

Products.asc file:

  code     price     description
  00-0011  500000    The Mona Lisa, one of the worlds great masterpieces.
                     Now at a reduced price!

The description for product 00-0011 will contain the contents of the
C<description> field on both lines, separated by a newline.

NOTE: Fields are separated by tabs, formatted for reading convenience.

This I<will> work for multiple fields in the same record. If the field
contains any non-empty value, it will be appended.

LINE is a special setting so that you can use a multi-line field.
Normally, when using the LINE type, you may have only data on one
line separated by one blank line. When using CONTINUE LINE, you may have
some number of fields which are each on a line, while the last one spans
multiple lines up until the first blank line.

Example in catalog.cfg:

  Database products products.txt  LINE
  Database products CONTINUE      LINE

Products.asc file:

    code
    price
    description
  
    00-0011
    500000
    The Mona Lisa, one of the worlds great masterpieces.
    Now at a reduced price!
  
    00-0011a
    1000
    A special frame for the Mona Lisa.

B<NOTES> reads a Lotus Notes "structured text" file. The format is
that there are any number of fields, all except one of which must
have a field name followed by a colon and then the data. There is
optional whitespace after the colon.

Records are separated by a settable delimiting charater which goes
on a line by itself, much like a "here document". By default it is
a form feed (^L) character.

The final field begins at the first blank line and continues to
the end of the record. This final field is named C<notes_field>
unless you set it as mentioned below.

MiniVend reads the field names from the first paragraph of the
file. The key field should be first, followed by other fields in
any order. If one (and only one) field name has whitespace, then
its name is used for the C<notes_field> and any characters after
a space or TAB are used as the record delimiter. If there are none,
then the delimiter returns to the default form feed (^L) and the
field name reverts to C<notes_field>. The field in question will
be discarded, but a second field with whitespace will cause an import
error.

Following records are then read by name, and only fields with
data in them need be set. Only the C<notes_field> may contain
a newline.  It is always the last field in the record, and begins
at the B<first> blank line.

The following example sets the delimiter to a tilde C<(~)> and
renames the C<notes_field> to C<description>.

Example in catalog.cfg:

  Database products products.txt  LINE
  Database products CONTINUE      NOTES

Products.asc file:

    code
    title
    price
    image
    description ~
    size
    color
  
    title: Mona Lisa
    price: 500000
    code: 00-0011
    image: 00-0011.jpg
  
    The Mona Lisa, one of the worlds great masterpieces.
    Now at a reduced price!
    ~
    title: The Art Store T-Shirt
    code: 99-102
    size: Medium, Large*, XL=Extra Large
    color: Green, Blue, Red, White*, Black
    price: 2000
  
    Extra large 1.00 extra.
    ~

=item EXCEL

Microsoft Excel is a widely-used tool to maintain MiniVend databases,
but has several problems with its standard TAB-delimited export, like
encasing fields containing commas in quotes, generating extra carriage
returns embedded in records, and not including trailing blank fields.
To avoid problems, use a text-qualifier of B<none>.

Set the EXCEL attribute to 1 to fix these problems on import:

    Database products EXCEL 1

This is normally used only with TAB-delimited files.

=back

=head2 Dictionary indexing with INDEX

MiniVend will automatically build index files for a fast binary search
of an individual field. This type of search is useful for looking up
the author of a book based on the beginning of their last name, a book
title based on its beginning, or other analagous situations.

Such a search requires a dictionary ordered index with the field to be searched
contained in the first field and the database key (product code) in the
second field. If you specify the C<INDEX field> modifier MiniVend will
build the index upon database import:

   Database  products  products.txt   TAB
   Database  products  INDEX          title

If the C<title> field is the fourth column in the C<products> database table,
a file C<products.txt.4> will be built, containing two tab-separated fields
something like:

    American Gothic   19-202
    Mona Lisa         00-0011
    Sunflowers        00-342
    The Starry Night  00-343

Options can be appended to the field name after a colon (:) --
the most useful will be C<f>, which does a case-insensitive sort. Careful,
you must use the C<mv_dict_fold> option to the search in that case.

Another option is C<c>, which stands for "comma index". If you want to
index on comma-separated sub-fields within a field, use the :c option:

   Database  products  products.txt   TAB
   Database  products  INDEX          category:c

This can get slow for larger databases and fields. MiniVend will
split the field on a comma (stripping surrounding whitespace) and
make index entries for each one. This allows multiple categories 
in one field while retaining the fast category search mechanism.
It might also be useful for a C<keywords> field.

The fast binary search is described in greater detail below -- see 
I<THE SEARCH ENGINE>.

=head2 MEMORY for memory-only databases

MiniVend's memory-based databases are the fastest possible way to organize
and store data you will frequently use. To force a database to be built
in memory instead of DBM, use the MEMORY modifier:

   Database  country  country.asc   TAB
   Database  country  MEMORY        1

Obviously large tables will use a great deal of memory, and the data
will need to be re-imported from the ASCII source file at every catalog
reconfiguration or MiniVend restart. The big advantage of using MEMORY
is that the database remains open at all times and does not need to be
reinitialized at every connect -- use it for smaller tables that will
be frequently accessed.

The MEMORY modifier forces IMPORT_ONCE.

=head2  IMPORT_ONCE

The IMPORT_ONCE modifier tells MiniVend not to re-import the database
from the ASCII file every time it changes. Normally, MiniVend does a 
comparison of the database file modification time with the ASCII source
every time it is accessed, and if the ASCII source is newer it will re-import
the file. IMPORT_ONCE tells it only to import on a server restart or catalog
reconfiguration:


   Database  products  products.txt   TAB
   Database  products  IMPORT_ONCE    1

SQL databases don't normally need this -- they will only be imported once
in normal operation. Also see I<NoImport> for a way to guarantee that the
table will never be imported.

IMPORT_ONCE is always in effect for MEMORY databases. Do a catalog
reconfiguration to force a change.

=head2 Importing in a page

You might often want to add a data record to a database as a result
of an order or other operation. Use MiniVend's C<[import ...]> tag.

=over 4

=item [import table type*] RECORD [/import]

Named attributes:

 [import table=table_name
         file=filename*
         type=(TAB|PIPE|CSV|%%|LINE)*
         continue=(NOTES|UNIX|DITTO)*
         separator=c*]

=back

Import one or more records into a database. The C<type> is any
of the valid MiniVend delimiter types, with the default being TAB.
The table must already be a defined
MiniVend database table; it cannot be created on the fly. (If you need
that, it is time to use SQL.)

The import type selected need not match the type the database was
specified; different delimiters may be used.

The C<type> of C<LINE> and C<continue> setting of C<NOTES> is particularly
useful, for it allows you to name your fields and not have to remember
the order in which they appear in the database. The following two imports
are identical in effect:

    [import table=orders]
             code: [value mv_order_number]
    shipping_mode: [shipping-description]
           status: pending
    [/import]
  
    [import table=orders]
    shipping_mode: [shipping-description]
    status:        pending
    code:          [value mv_order_number]
    [/import]

The C<code> or key must always be present, and is always named C<code>.

If you do not use C<NOTES> mode, you must import the fields in the
same order as they appear in the ASCII source file.

The C<file> option overrides the container text and imports directly
from a named file I<based in the catalog directory>. Careful,
if you want to import from C<products.txt> you must specify
C<file="products/products.txt">. If the I<NoAbsolute> directive
is set to C<Yes> in minivend.cfg, only relative path names will
be allowed.

The C<[import ....] TEXT [/import]> region may contain multiple records.
If using C<NOTES> mode, you must use a separator, which by default is
a form-feed character (^L). See I<Import Attributes> above for more
information.

=head2 Exporting from a database

To export your existing database to a file suitable for searching by
MiniVend, you can create an I<AdminPage> (or any page, for that matter)
that contains a C<[tag export ...][/tag]> element.

Perhaps a better method is to define the same sort of tags in an
I<OrderProfile>, and then use forms and buttons to access the 
profile.

=head2 Write Control

MiniVend databases can be written in the normal course of events, either
using the C<[import ...]> tag or with a tag like
C<[data table=table column=field key=code value=new-value]>.

If you wish to control writing of a global database, or to a certain
catalog within a series of subcatalogs, or make one read only, you can
do so.

To enable write control:

    Database   products  WRITE_CONTROL  1

Once that is done, you can make a database read only, which won't
allow writing even if C<[tag flag write]products[/tag]> is specified:

    Database   products  READ_ONLY  1

If you want to have control with C<[tag flag write]products[/tag]>:

    Database   products  WRITE_TAGGED  1

If you want to limit write to certain catalogs, you can set:

    Database   products  WRITE_CATALOG  simple=0, sample=1

The "simple" catalog will not be able to write, while "sample"
will if C<[tag flag write]products[/tag]> is enabled.

If you want a database to be always writable without having
to specify C<[tag flag write] ... [/tag]>, then you can
define:

    Database   products  WRITE_ALWAYS  1

The default behavior of SQL datbases is equivalent to
WRITE_ALWAYS, while the default for GDBM_File, DB_File,
and Memory databases is equivalent to:

    Database   products  WRITE_CONTROL 1
    Database   products  WRITE_TAGGED  1

=head2 Global Databases

If you have a database you want to make available to all catalogs
on the MiniVend server instance, you may define a database in 
minivend.cfg. Any catalog running under that server will be able to use
it.  Careful; it is writable by any catalog unless you use WRITE_CONTROL.

=head1 SQL SUPPORT

MiniVend can use any of a number of SQL databases through the powerful
Perl DBI/DBD access methods. This allows transparent access to any
database engine that is supported by a DBD module.  The current list
includes mSQL, mySQL, Solid, Postgres, Oracle, Sybase, Informix, Ingres,
Dbase, DB2, Fulcrum, and others. Any ODBC (with appropriate driver)
should also be supported.

No SQL database is included with MiniVend, but there are a number
widely available on the net, and many are free for non-commercial use.
Some examples include mSQL, mySQL, Sybase, and Qbase.

It is beyond the scope of this document to describe SQL or DBI/DBD,
and we will not attempt to. Sufficient familiarity is assumed.

In most cases, MiniVend cannot perform administrative functions like
creating a database or setting access permissions.  This must be done
with the tools provided with your SQL distribution.  But if given a
blank database and the permission to read and write it, MiniVend can
import ASCII files and bootstrap you from there.

=head2 SQL support via DBI

The configuration of the DBI database is done by setting attributes in
additional I<Database> directives B<after> the initial defining line as
described above. For example, the following defines the database B<arbitrary>
as a DBI database, sets the data source (DSN) to an appropriate value for an
mSQL database named C<minivend> on port 1114 of the local machine:

    Database arbitrary arbitrary.asc SQL
    Database arbitrary DSN           dbi:mSQL:minivend:localhost:1114

As a shorthand method, you can instead include the DSN as the type:

    Database arbitrary arbitrary.asc dbi:mSQL:minivend:localhost:1114

Supported configuration attributes include (but are not limited to):

=over 4

=item DSN

A specification of the DBI driver and its data source. To use the
DBD::mSQL driver for DBI, you would typically use:

    dbi:mSQL:minivend:othermachine.my.com:1112

where mSQL selects the driver (case IS important), C<minivend> selects the
database, C<othermachine.my.com> selects the host, and 1112 is the port.
On many systems, C<dbi:mSQL:minivend> will work just fine. (The C<minivend>
database must already exist, of course.)

This is the same as the B<DBI_DSN> environment variable -- if you don't set
the DSN parameter, then the value of B<DBI_DSN> will be used to
try and find the proper database to connect to.

=item USER

The user name you log into the database with -- same as the environment
variable B<DBI_USER>. If you don't need a user name, just don't set
the USER directive.

=item PASS

The password you log into the database with -- same as the environment
variable B<DBI_PASS>. If you don't need a password, just don't set
the PASS directive.

=item COLUMN_DEF

A comma-separated set of lines in the form NAME=TYPE(N), where NAME
is the name of the field/column, TYPE is the SQL data type reference,
and N is the length (if needed).  Most MiniVend fields should be of the
fixed-length character type, something like char(128). In fact that is the
default if you do not choose a type for a column.  You can have as many
lines as needed. This is not a DBI parameter, it is specific to MiniVend.

=item NAME

A space-separated field of column names for a table.  Normally not used --
MiniVend should resolve the column names properly upon query.  Set this if
your catalog errors out with "dbi: can't find field names" or the like.
The first field should always be B<code>. This is not a DBI parameter,
it is specific to MiniVend. All columns must be listed, in order of their
position in the table.

=item NUMERIC

Tells MiniVend to not quote values for this field; allows numeric data
types for SQL databases. Placed as a comma-separated field of column names
for a table, in no particular order. This should be defined if you are to
use an numeric value, as many DBD drivers do not yet support type queries.

=item UPPERCASE

Tells MiniVend to force field names to UPPER case for row accesses
using the C<[item-data ...]>, C<[loop-data ...]>, C<[item-field ...>,
etc. Typically used for Oracle and some other SQL implementations.

=item DELIMITER

A MiniVend delimiter type - one of TAB,CSV,PIPE,%%,LINE or the
corresponding numeric type. The default for SQL databases is TAB --
use DELIMITER if you wish to import another type. This is not a DBI
parameter, it is specific to MiniVend.

=item KEY

You can change the keying default of C<code> in the first column of the
database with the KEY directive. B<Don't use this unless you know exactly>
B<what you are doing and are prepared to alter all searches, imports, and>
B<exports accordingly.>  It is best to just accept the default and 
make the first column the key for any MiniVend database.

=item ChopBlanks,LongReadLen,LongTruncOK,RaiseError, etc.

Sets the corresponding DBI attribute. Of particular interest is ChopBlanks,
which should be set on drivers which by default return space-padded fixed-length
character fields (Solid is an example).

The supported list as of this release of MiniVend is:

  ChopBlanks
  CompatMode
  LongReadLen
  LongTruncOk
  PrintError
  RaiseError
  Warn

Issue the shell command C<perldoc DBI> for more information.

=back

Here is an example of a completely set up DBI database on mySQL, using
a comma-separated value input, setting the DBI attribute LongReadLen to
retrieve an entire field, and changing some field definitions from the
default char(128):


  Database   products  products.csv  dbi:mysql:minivend
  Database   products  USER          minivend
  Database   products  PASS          nevairbe
  Database   products  DELIMITER     CSV
 
  # Set a DBI attribute
  Database   products  LongReadLen   128
 
  # change some fields from the default field type of char(128)
  # Only applies if Minivend is importing from ASCII file
  # If you set a field to a numeric type, you must set the
  # NUMERIC attribute
  Database   products  COLUMN_DEF    "code=char(20) NOT NUL primary key"
  Database   products  COLUMN_DEF    price=float, discount=float
  Database   products  COLUMN_DEF    author=char(40), title=char(64)
  Database   products  COLUMN_DEF    nontaxable=char(3)
  Database   products  NUMERIC       price
  Database   products  NUMERIC       discount

You must have mySQL, DBI, and DBD::mysql completely installed and
tested, and have created the database C<minivend> for this to work.
Permissions are difficult on mySQL -- if you have trouble, try starting
the mySQL daemon with C<safe_mysqld --skip-grant-tables &> for testing
purposes.

To change to ODBC, the only changes required might
be:

    Database products  DSN         dbi:ODBC:TCP/IP localhost 1313
    Database products  ChopBlanks  1

The DSN setting is specific to your ODBC setup.  The B<ChopBlanks> setting takes
care of the space-padding in Solid and some other databases -- it is not
specific to ODBC.  Once again, DBI, DBD::ODBC, and the and appropriate ODBC
driver must be installed and tested.

=head2 SQL Access Methods

A MiniVend SQL database can be accessed with the same tags as any of
the other databases can. Arbitrary SQL queries can be passed with
the C<[query sql="SQL STATEMENT"]> MML tag.

=head2 Importing from an ASCII file

When importing a file for SQL, MiniVend by default uses the
first column of the ASCII file as the primary key, with a C<char(16)>
type, and assigns all other columns a C<char (128)> definition. These
definitions can be changed by placing the proper definitions in COLUMN_DEF
I<Database> directive attribute:

  Database  products  COLUMN_DEF  price=char(20), nontaxable=char(3)

You can set this as many times as desired if it will not fit on
the line nicely. 

  Database  products  COLUMN_DEF  price=char(20), nontaxable=char(3)
  Database  products  COLUMN_DEF  description=char(254)

To create an index automatically, you can append information when
the value is in quotes:

  Database  products  COLUMN_DEF  "code=char(14) primary key"

The field delimiter to use is TAB by default, but can be changed 
with the Database DELIMITER directive:

  Database  products products.csv dbi:mSQL:minivend:localhost:1114
  Database  products DELIMITER  CSV

If you wish to create other secondary keys to speed sorts and
searches you can do so in the COLUMN_DEF:

  Database  products COLUMN_DEF  "author=char(64) secondary key"

or use external database tools. Careful! Not all SQL databases use
the same index commands.

If you wish to use an existing SQL database instead of importing, set the
I<NoImport> directive in catalog.cfg to include any database identifiers you
never wish to import:

    NoImport  products inventory

B<WARNING:> If MiniVend has write permission on the products database,
you B<must> be careful to set the I<NoImport> directive or create the
proper .sql file. If that is not done, and the database source file
is changed, the SQL database could be overwritten. In any case,
always back up your database before enabling it for use by MiniVend.

=head1 MINIVEND TAG REFERENCE

 [accessories]     Access product accessory functions
 [ad]              Insert a random or rotating ad banner
 [area]            Insert a re-written MiniVend URL
 [bounce]          Insert header to redirect to a different URL
 [calc]            Perform Perl calculations (low overhead)
   [/calc]        
 [cart]            Set the current shopping cart name
 [cgi]             Insert a CGI form value
 [checked]         Conditionally check an HTML check/radio box
 [comment]         Insert comments in MiniVend pages
   [/comment]     
 [col]             Used with [row] -- rudimentary text tables for order reports
   [/col]         
 [condition]       Sets a condition inside [if explicit] and others
   [/condition]   
 [currency]        Formats a number like currency for current locale
   [/currency]    
 [data]            Access a database or user session element
 [default]         Insert a variable but with a default response if blank
 [description]     Output a product description
 [discount]        Set a product discount coupon
   [/discount]    
 [discount-price]  Show the discounted price
 [ecml]            Translate to-from Electronic Commerce Modeling Language field
 [else]            Defines else region for [if ...], [if-item-field ..] and others
   [/else]        
 [elsif]           Defines elsif region for [if ...]
   [/elsif]       
 [error]           Check/display form processing errors
 [field]           Access a product database field
 [file]            Insert the contents of a file
 [flag]            Set a minivend flag
 [fly-list]        Show an item "on-the-fly" in an arbitrary page
   [/fly-list]      
 [goto]            goto an arbitrary page location, skipping the rest
 [html_table]      create an HTML table from a query or list
 [if]              Perform any of many conditional tests
   [/if
 [if-ITEM-data]    Display region only if database element non-empty
   [/if-ITEM-data
 [if-ITEM-field]   Display region only if field non-empty
   [/if-ITEM-field
 [if-ITEM-param]   Display only if item passed parameter set
   [/if-ITEM-param
 [include]         Include a file with complete MiniVend interpretation
 [import]          Import ASCII text into a database
 [index]           Create a searchable ASCII index
 [input_filter]    Create an input filter
 [ITEM-accessories]Product accessory functions (set select box)
 [ITEM-alternate]  Alternation for table/line build
 [/ITEM-alternate
 [ITEM-change]     Grouping of items in list display
   [/ITEM-change
 [ITEM-code]       Insert current item SKU/code/row identifier
 [ITEM-data]       Insert data entry corresponding to current SKU
 [ITEM-description]Insert description corresponding to current SKU
 [ITEM-discount]   Show amount of discount for current SKU
 [ITEM-field]      Insert product database entry corresponding to current SKU
 [ITEM-increment]  Count for list
 [ITEM-last]       Stop displaying if condition is met
   [/ITEM-last]  
 [ITEM-next]       Skip item if condition is met
   [/ITEM-next]  
 [item-list]       Iterate over a shopping cart
   [/item-list]  
 [ITEM-param]      Show element from list.
 [ITEM-pos]        Show positional element from list.
 [ITEM-price]      Display price of item with any discounts/price breaks/adjustments
 [ITEM-quantity]   Show quantity ordered on shopping cart line
 [ITEM-subtotal]   Subtotal for the item (item-quantity * item-price)
 [label]           Set a label for goto
 [loop]            Iterate over an arbitrary list
   [/loop]       
 [matches]         Show number of matches from search
 [modifier-name]   Place a variable name that corresponds to an attribute
 [more]            Show region of search list only if more matches
 [more-list]       Display more matches list with links to next series
   [/more-list]  
 [mvasp]           ASP-style Perl programming area
 [no-match]        Define area of region results displayed when no match
   [/no-match]   
 [nitems]          Show number of items for a shopping cart
 [order]           Create HTML link to order an item
 [onfly]           Order an item "on-the-fly"
 [page]            Create A HREF with re-written URL to call MiniVend page
 [perl]            Embed output of arbitrary Perl in the page
   [/perl]       
 [price]           Show price of an item
 [process]         Create URL for MiniVend form processing
 [quantity-name]   Place a variable name that corresponds to item quantity
 [query]           Perform any of several types of SQL query
   [/query]       
 [read_cookie]     Read a cookie sent by the user
 [record]          Set a database record
 [region]          Define an area of the page as a search list/query
 [row]             Used with [col] -- rudimentary text tables for order reports
   [/row]        
 [salestax]        Show amount of salestax for shopping cart
 [scratch]         Access a scratch variable
 [search-list]     Display output of a MiniVend search
   [/search-list] 
 [selected]        Conditional selection of drop-down <SELECT ...>
 [set]             Set a scratch variable
   [/set]        
 [set_cookie]      Send a cookie to the user
 [setlocale]       Set the locale
 [shipping]        Calculate shipping
 [shipping-desc]   Show shipping description
 [sort]            Set sort order for iterating lists
   [/sort]       
 [strip]           Strip leading/trailing whitespace
 [subtotal]        Calculate subtotal without tax or shipping
 [tag]             Miscellaneous functions
   [/tag]        
 [then]            Define THEN region for [if ...]
   [/then]       
 [timed_build]     Build region of page on timed basis
 [total-cost]      Calculate order total with tax, handling, and shipping
 [userdb]          Access user database functions
 [update]          Perform MiniVend actions
 [value]           Display form value
 [value_extended]  Display form array values or do file upload functions


To build complex order forms and reports, MiniVend has a complete
tag language with over 80 different functions, dubbed B<MML>, for
B<M>inivend B<M>arkup B<L>anguage. It allows you access
to and control over any of an unlimited number of database tables,
multiple shopping carts, user name/address information, discount, tax, and
shipping information, search of files and databases, and much more.

There is some limited conditional capability with the C<[if ...]> tag, but
when doing complex operations you should strongly consider using embedded
Perl/ASP.

Most of the tests use Perl code, but MiniVend uses the Safe.pm module
with its default restrictions to help ensure that improper code will
not crash the server or modify the wrong data.

Perl can also be embedded with in the page, and if given the proper
permission by the system administrator, can call upon resources from
other computers and networks.

=head2 Tag syntax

MiniVend uses a style similar to HTML, but with [square brackets] replacing
<chevrons>. The parameters that can be passed are much the same, where
a parameter="paramter value" can be passed.

Summary:

    [tag parameter]             Tag called with positional parameter
    [tag parameter=value]       Tag called with named parameter
    [tag parameter="the value"] Tag called with space in parameter
    [tag 1 2 3]                 Tag called with multiple positional parameters
    [tag foo=1 bar=2 baz=3]     Tag called with multiple named parameters
    [tag foo=`2 + 2`]           Tag called with calculated parameter
    [tag foo="[value bar]"]     Tag called with tag inside parameter
    [tag foo="[value bar]"]
        Container text.         Container tag.
    [/tag]

Most tags can accept some positional parameters; this speeds up parsing and
is simpler to write in many cases.

Here is an example tag:

    [value name=city]

That will cause MiniVend to look in the user form value array and return
the value of the form parameter C<city>, which might have been set with:

    City: <INPUT TYPE=text NAME=city VALUE="[value city]">

Note that we pre-set the value with the previous value of C<city> (if any).
It uses the positional style -- C<name> is the first positional parameter
for the [value ...] tag.

Positional parameters cannot be derived from other MiniVend tags; for
instance [value [value formfield]] will not work. But if you use the
named parameter syntax, the parameters can contain other tags, i.e.:

    [value name="[value formfield]"]

There is one exception to the above rule when using the [item-list], 
[loop ...], [sql ...], and other list tags. We will examine that in
their individual sections.

Many MiniVend tags are container tags:

    [set Checkout]
        mv_nextpage=ord/checkout
        mv_todo=return
    [/set]

Tags and parameter names are not case sensitive, so [VALUE NAME=something]
would work just as well. MiniVend convention is to place HTML tags in upper
case and MiniVend tags in lower case so pages are easier to read, but you
could easily reverse the sense.

Single quotes work just as well as double quotes, and can prevent ambiguity.

    [value name=b_city set='[value city]']

works just as well.

Backticks, the other single quote, cause the parameter contents to be evaluated
as Perl code via the C<[calc]> tag. (This is in MV3.15 and above.) So

    [value name=row_value set=`$row_value += 1`] 

is the same as

    [value name=row_value set="[calc]$row_value += 1[/calc]"] 

You can also specify constructs inside an HTML tag:

    <TABLE MV="if items">
    <TR MV="item-list">
    <TD> [item-code] </TD>
    <TD> [item-description] </TD>
    <TD> [item-price] </TD>
    </TR></TABLE>

The above will loop over any items in the shopping cart, displaying
their part number, description, and price, but only IF there are items
in the cart. It is equivalent to:

    [if items]
    <TABLE>
    [item-list]
    <TR>
    <TD> [item-code] </TD>
    <TD> [item-description] </TD>
    <TD> [item-price] </TD>
    </TR>
    [/item-list]
    </TABLE>
    [/if]

This is provided to allow HTML editors to work with MiniVend if they wish.
One common tag that is best specified in this fashion is C<[body n]>, which
would be best done as <BODY MV="body 1">.

What is done with the results of the tag depends on whether it is a 
I<container> or I<standalone> tag. A container tag is one which has
an end tag, i.e. C<[tag] stuff [/tag]>. A standalone tag has no end 
tag, as in C<[area href=somepage]>.  (Note that C<[page ...]> and C<[order ..]>
are B<not> container tags.)

A container tag will have its output re-parsed for more MiniVend tags
by default. If you wish to inhibit this behavior, you must explicitly
set the attribute B<reparse> to 0.  Note that you will almost always
wish the default action.

With some exceptions (C<[include file]> among them) the output of a standalone
tag will not be re-interpreted for MiniVend tag constructs.

Most container tags will not have their contents interpreted before being
passed the container text. Exceptions include C<[calc]> .. C<[/calc]> and
C<[currency]> ... C<[/currency]>.  All tags accept the INTERPOLATE=1 tag
modifier, which causes the interpretation to take place. It is frequent that
you will B<not> want to interpret the contents of a C<[set variable]> TAGS
C<[/set]> pair, as that might contain tags which should only be upon evaluating
an order profile, search profile, or C<mv_click> operation. If you wish to
perform the evaluation at the time a variable is set, you would use
C<[set name=variable interpolate=1] TAGS [/set]>.

=head2 DATA and FIELD

The C<[data ...]> and C<[field ...]> tags access elements of MiniVend databases.
They are the form used outside of the iterating lists, and can be effectively
used to do lookups when the table, column/field or key/row is conditional based
on a previous operation.

The following are equivalent for attribute names:

    table ---> base
    col   ---> field --> column
    key   ---> code  --> row

The [field ...] tag is special in that it looks in any of the tables
defined as C<ProductFiles>, in that order, for the data, returning
the first non-empty value. In most catalogs, where C<ProductFiles> is
not defined (i.e. the demo), C<[field title 00-0011]> is equivalent to
C<[data products title 00-0011]>.

FAQ: Why doesn't [field col=foo key=bar] display something from my
table "category"?

It won't if:

    -- "category" is not in the directive ProductFiles
    -- You have multiple ProductFiles and an earlier one has an 
       entry for that key

=over 4 

=item [data table column key]

named attributes: C<[data base="database" field="field" key="key">
C<value="value" op="increment]>

Returns the value of the field in any of the arbitrary databases,
or from the variable namespaces. If the option
C<increment=1> is present, the field will be atomically incremented with
the value in B<value>.

If a DBM-based database is to be modified, it must be flagged writable
on the page calling the write tag. Use C<[tag flag write]products[/tag]>
to mark the C<products> database writable, for example.

In addition, the C<[data ...]> tag can access a number of elements in
the MiniVend session database:

    accesses      Accesses within the last 30 seconds
    arg           The argument passed in a [page ...] or [area ...] tag
    browser       The user browser string
    host          MiniVend's idea of the host (modified by DomainTail)
    last_error    The last error from the error logging
    last_url      The current MiniVend path_info
    logged_in     Whether the user is logged in via UserDB
    pageCount     Number of unique URLs generated
    prev_url      The previous path_info
    referer       HTTP_REFERER string
    ship_message  The last error messages from shipping
    source        Source of original entry to MiniVend
    time          Time (seconds since Jan 1, 1970) of last access
    user          The REMOTE_USER string
    username      User name logged in as (UserDB)

Databases will hide variables, so don't name a database "session",
"scratch", or any of the other reserved names or you won't be able
to use the C<[data ...]> tag to read them. Case is sensitive, so in
a pinch you could call the database "Session", but it would be better
not to.

=item [field name code]

named attributes: C<[field code="code" name="fieldname"]>

Expands into the value of the field I<name> for the product
identified by I<code> as found by searching the products database.
It will return the first entry found in the series of I<Product Files>.
the products database. If you want to constrain it to a particular
database, use the C<[data base name code]> tag.

=back

=head2 SET, SETI and SCRATCH

Scratch variables are maintained in the user session separate from the
form variable values set on HTML forms.

Many things can be controlled with scratch variables, notable search and
order processing, the C<mv_click> multiple variable setting facility, and
key MiniVend conditions like whether an item will be ordered on a separate
line.

There are two tags which are used to access the space, C<[set name]value[/set]>
and C<[scratch name]>.

=over 4

=item [set variable]value[/set]

named attributes: C<[set name="variable"] value [/set]>

Sets a scratchpad variable to I<value>.

Most of the mv_* variables that are used for search and order conditionals are
in another namespace -- they can be set by means of hidden fields in a
form.

You can set an order profile with:

  [set checkout]
  name=required You must give us your name.
  address=required Oops! No address.
  [/set]
  <INPUT TYPE=hidden NAME=mv_order_profile VALUE="checkout">

A search profile would be set with:

  [set substring_case]
  mv_substring_match=yes
  mv_case=yes
  [/set]
  <INPUT TYPE=hidden NAME=mv_profile VALUE="substring_case">

To do the same as C<[set foo]bar[/set]> in embedded Perl:

    [calc]$Scratch->{foo} = 'bar'; return;[/calc]

=item [seti variable][value something][/seti]

Same as C<[set] [/set]> except it interpolates the container text. THe
above is the same as:

    [set name=variable interpolate=1][value something][/set]

=item [scratch name]

Returns the contents of a scratch variable to the page. C<[scratch foo]>
is the same as, but faster than:

    [perl]$Scratch->{foo}[/perl]

=item [if scratch name op* compare*] yes [else] no [/else] [/if]

Tests a scratch variable (see I<IF>).

=back

=head2 LOOP

Loop lists can be used to construct arbitrary lists based on the contents
of a database field, a search, or other value (like a fixed list).

Loop accepts a C<search> parameter which will do one-click searches
on a database table (or file).

To iterate over all keys in a table, use the idiom
(C<[loop search="ra=yes/ml=9999"] [/loop]>.  C<ra=yes> sets C<mv_return_all>,
which means "match everything".  The C<ml=9999> limits matches to that
many records -- you normally don't want to use that many, but it is a
reasonable default maximum. If you want to not use the text file for
searching a Minivend DBM database, don't forget to use C<st=db>
(mv_searchtype).

Both can be sorted with C<[sort table:field:mod -start +number]> modifiers.
See I<SORTING>.

=over 4

=item [loop item item item] LIST [/loop]

named attributes: C<[loop prefix=label* list="item item item"* search="se=whatever"*]>

Returns a string consisting of the LIST, repeated for every item in a
comma-separated or space-separated list. Operates in the same fashion
as the C<[item-list]> tag, except for order-item-specific values. Intended
to pull multiple attributes from an item modifier -- but can be useful
for other things, like building a pre-ordained product list on a page.

Loop lists can be nested by using different prefixes:

    [loop prefix=size list="Small Medium Large"]
        [loop prefix=color list="Red White Blue"]
            [color-code]-[size-code]<BR>
        [/loop]
        <P>
    [/loop]

This will output:

                Red-Small
                White-Small
                Blue-Small
        
                Red-Medium
                White-Medium
                Blue-Medium
        
                Red-Large
                White-Large
                Blue-Large

You can do an arbitrary search with the search="args" parameter, just
as in a one-click search:

    [loop search="se=Americana/sf=category"]
        [loop-code] [loop-field title]
    [/loop]

The above will show all items with a category containing the whole world
"Americana".

=item [if-loop-data table field] IF [else] ELSE [/else][/if-loop-field]

Outputs the IF if the C<field> in C<table> is non-empty, and the ELSE (if any)
otherwise.

NOTE: This tag does not nest with other C<[if-loop-data ...]> tags.

=item [if-loop-field field] IF [else] ELSE [/else][/if-loop-field]

Outputs the B<IF> if the C<field> in the C<products> table is non-empty,
and the B<ELSE> (if any) otherwise.

NOTE: This tag does not nest with other C<[if-loop-field ...]> tags.

=item [loop-accessories]

Evaluates to the value of the Accessories database entry for
the item.

=item [loop-alternate N] DIVISIBLE [else] NOT DIVISIBLE [/else][/loop-alternate]

Set up an alternation sequence. If the loop-increment is divisible by C<N>,
the text will be displayed. If an C<[else]NOT DIVISIBLE TEXT[/else]> is present,
then the NOT DIVISIBLE TEXT will be displayed.

For example:

    [loop-alternate 2]EVEN[else]ODD[/else][/loop-alternate]
    [loop-alternate 3]BY 3[else]NOT by 3[/else][/loop-alternate]

=item [/loop-alternate]

Terminate the alternation area.

=item [loop-change marker]

Same as C<[item-change]> but within loop lists.

=item [loop-code]

Evaluates to the product code for the current item.

=item [loop-data database fieldname]

Evaluates to the field name I<fieldname> in the arbitrary database
table I<database>, for the current item.

=item [loop-description]

Evaluates to the product description (from the products file)
for the current item.

=item [loop-field fieldname]

The [loop-field ...] tag is special in that it looks in any of the tables
defined as C<ProductFiles>, in that order, for the data, returning
the value only if that key is defined. In most catalogs, where C<ProductFiles> is
not defined (i.e. the demo), C<[loop-field title]> is equivalent to
C<[loop-data products title]>.

Evaluates to the field name I<fieldname> in the database,  for
the current item.

=item [loop-increment]

Evaluates to the number of the item in the list. Used
for numbering items in the list.

=item [loop-last]tags[/loop-last]

Evaluates the output of the MiniVend tags encased inside,
and if it evaluates to a numerical non-zero number (i.e. 1, 23, or -1)
then the loop iteration will terminate. If the evaluated number is
B<negative>, then the item itself will be skipped. If the evaluated
number is B<positive>, then the item itself will be shown but will be
last on the list.

      [loop-last][calc]
        return -1 if '[loop-field weight]' eq '';
        return 1 if '[loop-field weight]' < 1;
        return 0;
        [/calc][/loop-last]

If this is contained in your C<[loop list]> and the weight field is empty,
then a numerical C<-1> will be output from the C<[calc][/calc]> tags; the
list will end and the item will B<not> be shown. If the product's weight
field is less than 1, a numerical 1 is output.  The item will be shown,
but will be the last item shown.

=item [loop-next]tags[/loop-next]

Evaluates the output of the MiniVend tags encased inside, and
if it evaluates to a numerical non-zero number (i.e. 1, 23, or -1) then
the loop will be skipped with no output. Example:

      [loop-next][calc][loop-field weight] < 1[/calc][/loop-next]

If this is contained in your C<[loop list]> and the product's weight
field is less than 1, then a numerical C<1> will be output from the
C<[calc][/calc]> operation. The item will not be shown.

=item [loop-price n* noformat*]

Evaluates to the price for optional quantity n (from the products file)
of the current item, with currency formatting. If the optional "noformat"
is set, then currency formatting will not be applied.

=back

=head2 IF

=over 4

=item [if type field op* compare*]

named attributes: C<[if type="type" term="field" op="op" compare="compare"]>

=item [if !type field op* compare*]

named attributes: C<[if type="!type" term="field" op="op" compare="compare"]>

=back

Allows conditional building of HTML based on the setting of various MiniVend
session and database values. The general form is:

    [if type term op compare]
    [then]
                                If true, this is printed on the document.
                                The [then] [/then] is optional in most
                                cases. If ! is prepended to the type
                                setting, the sense is reversed and
                                this will be output for a false condition.
    [/then]
    [elsif type term op compare]
                                Optional, tested when if fails
    [/elsif] 
    [else]
                                Optional, printed when all above fail
    [/else]
    [/if]

The C<[if]> tag can also have some variants:

    [if explicit][condition] CODE [/condition]
                Displayed if valid Perl CODE returns a true value.
    [/if]

You can do some Perl-style regular expressions, and combine conditions:

    [if value name =~ /^mike/i]
                                This is the if with Mike.
    [elsif value name =~ /^sally/i]
                                This is an elsif with Sally.
    [/elsif]
    [elsif value name =~ /^barb/i]
    [or value name =~ /^mary/i]
                                This is an elsif with Barb or Mary.
    [elsif value name =~ /^pat/i]
    [and value othername =~ /^mike/i]
                                This is an elsif with Pat and Mike.
    [/elsif]
    [else]
                                This is the else, no name I know.
    [/else]
    [/if]

While the named parameter tag syntax works for C<[if ...]>, it is
more convenient to use the positional syntax in most cases.  The only
exception is if you are planning on doing a test on the results of
another tag sequence:

    [if value name =~ /[value b_name]/]
        Shipping name matches billing name.
    [/if]

Oops!  This will not work. You must do instead

    [if type=value term=name op="=~" compare="/[value b_name]/"]
        Shipping name matches billing name.
    [/if]

or

    [if type=value term=high_water op="<" compare="[shipping]"]
        Shipping cost is too high, charter a truck.
    [/if]

If you wish to do AND and OR operations, you will have to use 
C<[if explicit]>. This allows complex testing and parsing of
values.

There are many test targets available:

=over 4

=item config Directive

The MiniVend configuration variables. These are set
by the directives in your MiniVend configuration file (or
the defaults).

    [if config CreditCardAuto]
    Auto credit card validation is enabled.
    [/if]

=item data  database::field::key

The MiniVend databases. Retrieves a field in the database and
returns true or false based on the value.

    [if data products::size::99-102]
    There is size information.
    [else]
    No size information.
    [/else]
    [/if]

    [if data products::size::99-102 =~ /small/i]
    There is a small size available.
    [else]
    No small size available.
    [/else]
    [/if]

If you want to use another tag to select the key, and it is not
a looping tag construct, you must use named parameters:

	[set code]99-102[/set]
    [if type=data term="products::size::[scratch code]"]
    There is size information.
    [else]
    No size information.
    [/else]
    [/if]

=item discount

Checks to see if a discount is present for an item.

    [if discount 99-102]
    Item is discounted.
    [/if]

=item explicit

A test for an explicit value. If perl code is placed between
a C<[condition]> C<[/condition]> tag pair, it will be used to make
the comparison. Arguments can be passed to import data from
user space, just as with the C<[perl]> tag.

    [if explicit]
    [condition]
        $country = '[value country]';
        return 1 if $country =~ /u\.?s\.?a?/i;
        return 0;
    [/condition]
    You have indicated a US address.
    [else]
    You have indicated a non-US address. 
    [/else]
    [/if]

This example is a bit contrived, as the same thing could be
accomplished with C<[if value country =~ /u\.?s\.?a?/i]>, but
you will run into many situations where it is useful.

This will work for I<Variable> values:

    [if explicit "__MYVAR__"] .. [/if]

=item file

Tests for existence of a file. Useful for placing image
tags only if the image is present.

    [if file /home/user/www/images/[item-code].gif]
    <IMG SRC="[item-code].gif">
    [/if]

    or 

    [if type=file term="/home/user/www/images/[item-code].gif"]
    <IMG SRC="[item-code].gif">
    [/if]

The C<file> test requires that the I<SafeUntrap> directive contains
C<ftfile> (which is the default).

=item items

The MiniVend shopping carts. If not specified, the cart
used is the main cart. Usually used as a litmus test to
see if anything is in the cart, for example:

  [if items]You have items in your shopping cart.[/if]
  
  [if items layaway]You have items on layaway.[/if]

=item ordered

Order status of individual items in the MiniVend shopping
carts. If not specified, the cart used is the main cart.
The following items refer to a part number of 99-102.

  [if ordered 99-102] ... [/if]
    Checks the status of an item on order, true if item
    99-102 is in the main cart.

  [if ordered 99-102 layaway] ... [/if]
    Checks the status of an item on order, true if item
    99-102 is in the layaway cart.

  [if ordered 99-102 main size] ... [/if]
    Checks the status of an item on order in the main cart,
    true if it has a size attribute.

  [if ordered 99-102 main size =~ /large/i] ... [/if]
    Checks the status of an item on order in the main cart,
    true if it has a size attribute containing 'large'.
    THE CART NAME IS REQUIRED IN THE OLD SYNTAX. The new
    syntax for that one would be:

    [if type=ordered term="99-102" compare="size =~ /large/i"]

    To make sure it is exactly large, you could use:

    [if ordered 99-102 main size eq 'large'] ... [/if]

  [if ordered 99-102 main lines] ... [/if]
      Special case -- counts the lines that the item code is
      present on. (Only useful, of course, when mv_separate_items
      or SeparateItems is defined.)

=item salestax

The salestax database.

    [if salestax [value state] > 0]
    There is salestax for your state.
    [else]
    No salestax for your state.
    [/else]
    [/if]

Key matching is case-insensitive.

=item scratch

The MiniVend scratchpad variables, which can be set
with the C<[set name]value[/set]> element. 

    [if scratch mv_separate_items]
    Ordered items will be placed on a separate line.
    [else]
    Ordered items will be placed on the same line.
    [/else]
    [/if]

=item session

The MiniVend session variables. Of particular interest are I<logged_in>,
I<source>, I<browser>, and I<username>.

=item shipping

The shipping database.

=item validcc

A special case, takes the form C<[if validcc no type exp_date]>.
Evaluates to true if the supplied credit card number, type
of card, and expiration date pass a validity test. Does
a LUHN-10 calculation to weed out typos or phony 
card numbers.

=item value

The MiniVend user variables, typically set in search,
control, or order forms. Variables beginning with C<mv_>
are MiniVend special values, and should be tested/used
with caution.

=back

The I<field> term is the specifier for that area. For example,
C<[if session frames]> would return true if the C<frames>
session parameter was set.

As an example, consider buttonbars for frame-based setups. It would be
nice to display a different buttonbar (with no frame targets) for sessions
that are not using frames:

    [if session frames]
        [buttonbar 1]
    [else]
        [buttonbar 2]
    [/else]
    [/if]

Another example might be the when search matches are displayed. If
you use the string C<[value mv_match_count] titles found>, it will display
a plural for only one match. Use:

    [if value mv_match_count != 1]
        [value mv_match_count] matches found.
    [else]
        Only one match was found.
    [/else]
    [/if]

The I<op> term is the compare operation to be used. Compare operations are
as in Perl:

    ==  numeric equivalence
    eq  string equivalence
    >   numeric greater-than
    gt  string greater-than
    <   numeric less-than
    lt  string less-than
    !=  numeric non-equivalence
    ne  string equivalence

Any simple perl test can be used, including some limited regex matching.
More complex tests are best done with C<[if explicit]>.

=over 4

=item [then] text [/then]

This is optional if you are not nesting if conditions, as the text
immediately following the C<[if ..]> tag is used as the conditionally
substituted text. If nesting C<[if ...]> tags you should use a C<[then][/then]>
on any outside conditions to ensure proper interpolation.

=item [elsif type field op* compare*]

named attributes: C<[elsif type="type" term="field" op="op" compare="compare"]>

Additional conditions for test, applied if the initial C<[if ..]> test
fails.

=item [else] text [/else]

The optional else-text for an if or if-item-field conditional.

=item [condition] text [/condition]

Only used with the C<[if explicit]> tag. Allows an arbitrary expression
B<in Perl> to be placed inside, with its return value interpreted as
the result of the test. If arguments are added to C<[if explicit args]>,
those will be passed as arguments are in the C<[perl]> construct.

=item [/if]

Terminates an if conditional.

=back

=head2 TAG -- the catch all

Many MiniVend functions can be controlled or specified with C<[tag ...][/tag]>
pairs.

Named syntax:

    [tag op=operation arg="arg1 arg2 ..."]other[/tag]

=over 4

=item [tag arg* arg*]text[/tag]

Performs any of a number of operations, based on the presence of C<arg>.
The arguments that may be given are:

=item each database

Returns a loop-list with every key in C<database> evaluated
as the C<[loop-code]>. This will return the key and field C<name>
for every record in the C<products> database:

    [tag each products][loop-code]  [loop-field name]<BR>[/tag]

=item export database file* type*

Exports a complete MiniVend database to its text source file (or any
specified file). The integer C<n>, if specified, will select export in
one of the enumerated MiniVend export formats. The following tag will
export the products database to products.txt (or whatever you have
defined its source file as), in the format specified by the
I<Database> directive:

    [tag export products][/tag]

Same thing, except to the file products/new_products.txt:

    [tag export products products/newproducts.txt][/tag]

Same thing, except the export is done with a PIPE delimiter:

    [tag export products products/newproducts.txt 5][/tag]

The file is relative to the catalog directory, and only may be
an absolute path name if I<NoAbsolute> is set to C<No>.

=item flag arg

Sets a MiniVend condition.

The following enables writes on the C<products> and C<sizes> databases
held in MiniVend internal DBM format:

    [tag flag write]products sizes[/tag]

SQL databases are always writable if allowed by the SQL database itself --
in-memory databases will never be written.

The C<[tag flag build][/tag]> combination forces static build of a page, even
if dynamic elements are contained. Similarly, the C<[tag flag cache][/tag]>
forces search or page caching (not usually wise).

=item log dir/file

Logs a message to a file, fully interpolated for MiniVend tags.
The following tag will send every item code and description in the user's
shopping cart to the file logs/transactions.txt:

    [tag log logs/transactions.txt]
    [item_list][item-code]  [item-description]
    [/item_list][/tag]

The file is relative to the catalog directory, and only may be
an absolute path name if I<NoAbsolute> is set to C<No>.

=item mime description_string

Returns a MIME-encapsulated message with the boundary as employed
in the other mime tags, and the C<description_string> used as the 
Content-Description. For example

   [tag mime My Plain Text]Your message here.[/tag]

will return

  Content-Type: TEXT/PLAIN; CHARSET=US-ASCII
  Content-ID: [sequential, lead as in mime boundary]
  Content-Description: My Plain Text
  
  Your message here.

C<When used in concert with [tag mime boundary]C<>, [tag mime header]>, and
C<[tag mime id]>, allows MIME attachments to be included -- typically with
PGP-encrypted credit card numbers. See the demo page ord/report.html
for an example.

=item mime boundary

Returns a MIME message boundary with unique string keyed on
session ID, page count, and time.

=item mime header

Returns a MIME message header with the proper boundary for that
session ID, page count, and time.

=item mime id

Returns a MIME message id with the proper boundary for that
session ID, page count, and time.

=item time

Formats the current time according to POSIX strftime arguments.
The following is the string for Wednesday, September 1, 1999. 

    [tag op=time]%A, %B %d, %Y[/tag]

Options
=item touch 

Touches a database to allow use of the tag_data() routine in 
user-defined subroutines.  If this is not done, the routine
will error out if the database has not previously been accessed
on the page.

    [tag touch products][/tag]

=back

=head2 COMMENT

=over 4

=item [comment] code [/comment]

Comments out MiniVend tags (and anything else) from a page. The 
contents are not displayed unless DisplayComments is set in minivend.cfg.
Can be nested.

=back

=head2 User-defined Tags

To define a tag that is catalog-specific, place I<UserTag> directives in
your catalog.cfg file. For server-wide tags, define them in minivend.cfg.
Catalog-specific tags take precedence if both are defined -- in fact,
you can override the base MiniVend tag set with them. The directive
takes the form:

   UserTag  tagname  property  value

where C<tagname> is the name of the tag, C<property> is the attribute
(described below), and C<value> is the value of the property for that
tagname.

The user tags can either be based on Perl subroutines or just be
aliases for existing tags. Some quick examples are below.

An alias:

    UserTag product_name Alias     data products title

This will change C<[product_name 99-102]> into C<[data products title 99-102]>,
which will output the C<title> database field for product code C<99-102>.
Don't use this with C<[item-data ...]> and C<[item-field ...]>, as they
are parsed separately.  You can do C<[product-name [item-code]]>, though.

A simple subroutine:

    UserTag company_name Routine   sub { "Your company name" }

When you place a C<[company-name]> tag in a MiniVend page, the text 
C<Your company name> will be substituted.

A subroutine with a passed text as an argument:

    UserTag caps   Routine   sub { return "\U@_" }
    UserTag caps   HasEndTag 

The tag C<[caps]This text should be all upper case[/caps]> will become
C<THIS TEXT SHOULD BE ALL UPPER CASE>.

Here is a useful one you might wish to use:

    UserTag quick_table HasEndTag
    UserTag quick_table Interpolate
    UserTag quick_table Order   border
    UserTag quick_table Routine <<EOF
    sub {
        my ($border,$input) = @_;
        $border = " BORDER=$border" if $border;
        my $out = "<TABLE ALIGN=LEFT$border>";
        my @rows = split /\n+/, $input;
        my ($left, $right);
        for(@rows) {
            $out .= '<TR><TD ALIGN=RIGHT VALIGN=TOP>';
            ($left, $right) = split /\s*:\s*/, $_, 2;
            $out .= '<B>' unless $left =~ /</;
            $out .= $left;
            $out .= '</B>' unless $left =~ /</;
            $out .= '</TD><TD VALIGN=TOP>';
            $out .= $right;
            $out .= '</TD></TR>';
            $out .= "\n";
        }
        $out .= '</TABLE>';
    }
    EOF

Called with:

    [quick-table border=2]
    Name: [value name]
    City: [value city][if value state], [value state][/if] [value country]
    [/quick_table]

The properties for UserTag are are:

=over 4

=item AddAttr

Add the attribute hash to the parameters already defined to be sent
by the I<Order> setting. This includes the canned attributes C<true>,
C<false>, C<undef>, C<interpolate>, and any other attributes you
have set in the tag. Allows your tag routine to take a hash reference
with the important parameters. Example:

    UserTag echo-params AddAttr
    UserTag echo-params Routine <<EOR
    sub {
        my($ref) = @_;
        my $out;
        for (sort keys %$ref) {
            # skip these meaningless parameters
            next if /^(undef|true|false)$/;

            $out .= "$_=";
            $out .= '"';
            $out .= $ref->{$_};
            $out .= '"'
            $out .= "\n";
        }
        return $out;
    }
    EOR

If you define the above UserTag and put put this on a MiniVend page

    <PRE>
    [echo-params  Param1=1 param2=2 param3=three]
    </PRE>

the resulting output will be:

    interpolate="0"
    param1="1"
    param2="2"
    param3="three"

The C<interpolate> parameter is always present for every tag, and
defines the behavior of container text or output depending on the 
value of C<HasEndTag>.

=item Alias

An alias for an existing (or other user-defined) tag. It takes the
form:

    UserTag tagname Alias    tag to insert

An Alias is the only property that does not require a I<Routine>
to process the tag.

=item attrAlias

An alias for an existing attribute for defined tag. It takes the
form:

    UserTag tagname attrAlias   alias attr

As an example, the standard MiniVend C<value> tag takes a named
attribute of C<name> for the variable name, meaning that C<[value name=var]>
will display the value of form field C<var>. If you put this line
in catalog.cfg:

    UserTag value attrAlias   identifier name

then C<[value identifier=var]> will be an equivalent tag.

=item CanNest

Notifies MiniVend that this tag must be checked for nesting.
Only applies to tags that have I<HasEndTag> defined, of course.
NOTE: Your routine must handle the subtleties of nesting, so
don't use this unless you are quite conversant with parsing
routines.  See the routines C<tag_loop_list> and C<tag_if> in 
lib/Vend/Interpolate.pm for an example of a nesting tag.

    UserTag tagname CanNest

=item Gobble

If there is no end tag present for a container tag, the normal behavior
is to not process the tag call. If you set C<Gobble>, the tag will
work for all the remaining text on the page. The C<timed-build> and
C<mvasp> tags are the tags MiniVend defines to C<Gobble>.

=item HasEndTag

Defines an ending C<[/tag]> to encapsulate your text -- the text in
between the beginning C<[tagname]> and ending C<[/tagname]> will
be the last argument sent to the defined subroutine.

    UserTag tagname HasEndTag

=item Implicit

This defines an attribute as implicit, meaning it can just be an C<attribute> 
instead of an C<attribute=value> pair. It must be a recognized attribute
in the tag definition, or there will be big problems. Use this with caution!

    UserTag tagname Implicit attribute value

If you want to set a standard include file to a fixed value by default,
but don't want to have to specify C<[include file="/long/path/to/file"]>
every time, you can just put:

    UserTag include Implicit file file=/long/path/to/file

and C<[include file]> will be the equivalent. You can still specify
another value with C<[include file="/another/path/to/file"]>

=item Interpolate

The behavior for this attribute depends on whether the tag is a container
(i.e. C<HasEndTag> is defined). If it is not a container, the C<Interpolate>
attribute causes the B<the resulting HTML> from the C<UserTag> will be
re-parsed for more MiniVend tags.  If it is a container, C<Interpolate>
causes the contents of the tag to be parsed B<before> the tag routine
is run.

    UserTag tagname Interpolate

=item InvalidateCache

If this is defined, the presence of the tag on a page will prevent
search cache, page cache, and static builds from operating on the
page.

    UserTag tagname InvalidateCache

It does not override C<[tag flag build][/tag]>, though.

=item Order

The optional arguments that can be sent to the tag. This defines not only
the order in which they will be passed to I<Routine>, but the name of
the tags. If encapsulated text is appropriate (I<HasEndTag> is set),
it will be the last argument.

    UserTag tagname Order param1 param2

=item PosRoutine

Identical to the Routine argument -- a subroutine that will be called
when the positional call is made, i.e. C<[usertag argument]> instead
of C<[usertag ARG=argument]>. If not defined, I<Routine> is used, and
MiniVend will usually do the right thing.

=item ReplaceAttr

Works in concert with InsertHTML, defining a B<single> attribute which
will be replaced in the insertion operation.

  UserTag tagname ReplaceAttr  htmltag attr

An example is the standard HTML <A HREF=...> tag. If you want to use the
MiniVend tag C<[area pagename]> inside of it, then you would normally
want to replace the HREF attribute. So the equivalent to the following
is defined within MiniVend:

  UserTag  area  ReplaceAttr  a  href

Causing this

    <A MV="area pagename" HREF="a_test_page.html">

to become

    <A HREF="http://yourserver/cgi/simple/pagename?X8sl2lly;;44">

when intepreted.

=item Routine

An inline subroutine that will be used to process the arguments of the tag. It
must not be named, and will be allowed to access unsafe elements only if
the C<minivend.cfg> parameter I<AllowGlobal> is set for the catalog.

    UserTag tagname Routine  sub { "your perl code here!" }

The routine may use a "here" document for readability:

    UserTag tagname Routine <<EOF
    sub {
        my ($param1, $param2, $text) = @_;
        return "Parameter 1 is $param1, Parameter 2 is $param2";
    }
    EOF

The usual I<here documents> caveats apply.

Parameters defined with the I<Order> property will be sent to the routine
first, followed by any encapsulated text (I<HasEndTag> is set).

=back

Note that the UserTag facility, combined with AllowGlobal, allows the
user to define tags just as powerful as the standard MiniVend tags.
This is not recommended for the novice, though -- keep it simple. 8-)

=head2 PRICE, DESCRIPTION, ACCESSORIES

=over 4

=item [price code quantity* database* noformat*]

named attributes: C<[price
        code="code"
        quantity="N"
        base="database"
        noformat=1*
        optionX="value"]>

Expands into the price of the product identified by code as found in
the products database. If there is more than one products file defined,
they will be searched in order unless constrained by the optional
argument B<base>. The optional argument B<quantity> selects an entry
from the quantity price list. To receive a raw number, with no currency
formatting, use the option C<noformat=1>.

If an named attribute corresponding to a product option is passed, and
that option would cause a change in the price, the appropriate price
will be displayed.

DEMO EXAMPLE: The T-Shirt (product code 99-102), with a base price of
$10.00, can vary in price depending on size and color. C<S>, the small
size, is 50 cents less; C<XL>, the extra large size, is $1.00 more, and
the color C<RED> is 0.75 extra. There are also quantity pricing breaks
(see the demo C<pricing> database. So the following will be true:

    [price  code=99-102
            size=L]              is $10.00
 
    [price  code=99-102
            size=XL]             is $11.00
 
    [price  code=99-102
            color=RED
            size=XL]             is $11.75
 
    [price  code=99-102
            size=XL
            quantity=10]         is $10.00
 
    [price  code=99-102
            size=S]              is $9.50

An illustration of this is on the simple C<flypage> template when passed
that item code.

=item [description code database*]

named attributes: C<[description code="code" base="database"]>

Expands into the description of the product identified by code as found in
the products database. If there is more than one products file defined,
they will be searched in order unless constrained by the optional
argument B<base>.

=item [accessories code attribute*, type*, field*, database*, name*, outboard*]

named attributes: C<[accessories code="code">
C<arg="attribute*, type*, field*, database*, name*, outboard*"]>

If not given one of the optional arguments, expands into the value
of the accessories database entry for the product
identified by I<code> as found in the products database.

If passed any of the optional arguments, initiates special processing
of item attributes based on entries in the product database.

See I<Item Attributes> for a complete description of the arguments.

When called with an attribute, the database is consulted and looks for
a comma-separated list of attribute options. They take the form:

    name=Label Text, name=Label Text*

The label text is optional -- if none is given, the B<name> will
be used.

If an asterisk is the last character of the label text, the item is
the default selection. If no default is specified, the first will be
the default. An example:

    [accessories TK112 color]

This will search the product database for a field named "color". If
an entry "beige=Almond, gold=Harvest Gold, White*, green=Avocado" is found,
a select box like this will be built:

    <SELECT NAME="mv_order_color">
    <OPTION VALUE="beige">Almond
    <OPTION VALUE="gold">Harvest Gold
    <OPTION SELECTED>White
    <OPTION VALUE="green">Avocado
    </SELECT>

In combination with the C<mv_order_item> and C<mv_order_quantity> variables
this can be used to allow entry of an attribute at time of order.

=back

=head2 FILE and INCLUDE

These elements read a file from the disk and insert the contents
in the location of the tag. C<[include ...]> will allow insertion of
MiniVend variables.

=over 4

=item [file ...]

named: [file name="name" type="dos|mac|unix"*]

positional: [file name]

Inserts the contents of the named file. The file should normally
be relative to the catalog directory -- file names beginning with
/ or .. are only allowed if the MiniVend server administrator
has disabled I<NoAbsolute>.

The optional C<type> parameter will do an appropriate ASCII translation
on the file before it is sent.

=item [include file]

named attributes: C<[include file="name"]>

B<NOTE:> New to MiniVend 3.04.

Same as C<[file name]> except interpolates for all MiniVend tags
and variables.

=back

=head2 Banner/Ad rotation

MiniVend has a built-in banner rotation system designed to show ads
or other messages according to category and an optional weighting.

The C<[banner ...]> MML tag is used to implement it.

The weighting system pre-builds banners in the directory 'Banners',
under the temporary directory. It will build one copy of the banner
for every one weight. If you weight one banner C<7>, one C<2>, and
one C<1>, then a total of 10 pre-built banners will be made. The first
will be displayed 70% of the time, the second 20%, and the third 10%,
in random fashion. If you want all equal, place an equal weight of 1.

Each category may have separate weighting. If you place the above
in category C<tech>, then it will behave as above when you place
C<[banner category=tech]> in the page. A separate category, say C<art>,
would have its own rotation and weighting.

The C<[banner ...]> tag is based on a database table, named C<banners>
by default.  It expects a total of five (5) fields in the table:

=over 4

=item code

This is the key for the item. If the banners are not weighted, then
this should be a category specific code.

=item category

If you choose to categorize weighted ads, this contains the category
to select. If empty, it will be placed in the default (or blank) category.

=item weight

Must be an integer number 1 or greater to include this ad in the weighting.
If 0 or blank, the ad will be ignored when weighted ads are built.

=item rotate

If the weighted banners are not used, this must contain some value.
If the field is empty, the banner will not be displayed.
If the value is specifically C<0> (zero), then the entire contents of the 
C<banner> field will be displayed when this category is used. If it
is non-zero, then the contents of the C<banner> field will be split
into segments (by the separator C<{or}>). For each segment, the banners
will rotate in sequence I<for that user only>. Obviously, the first
banner in the sequence is more likely to be displayed than the last.

Summary of values of rotate field:

    non-zero, non-blank: Rotating ads
    blank:               Ad not displayed
    0:                   Ad is entire contents of banner field

=item banner

This contains the banner text. If more than one banner is in the field,
they should be separated by the text C<{or}> (which will not be displayed).

=back

Minivend expects the C<banner> field to contains the banner text. It
can contain more than one banner, separated by the string '{or}'. To
activate the ad, place any string in the field C<rotate>.

The special key "default" is the banner that is displayed if no banners are
found. (Doesn't apply to weighted banners.)

Weighted banners are built the first time they are accessed after catalog
reconfiguration. They will not be rebuilt until either:

    -- the catalog is reconfigured
    -- the file tmp/Banners/total_weight or
       tmp/Banners/<category>/total_weight is removed

If the option C<once> is passed (i.e. [banner once=1 weighted=1] then
the banners will not be rebuilt until the C<total_weight> file is
removed.

The database specification should make the C<weight> field numeric so
that the proper query can be made. Here is the example from MiniVend's 
demo:

    Database   banner   banner.txt   TAB
    Database   banner   NUMERIC      weight

Examples:

=over 4

=item weighted, categorized

To select categorized I<and> weighted banners:

Your C<banner> table would look like:

    code    category   weight   rotate   banner
    t1      tech       1                 Click here for a 10% banner
    t2      tech       2                 Click here for a 20% banner
    t3      tech       7                 Click here for a 70% banner
    a1      art        1                 Click here for a 10% banner
    a2      art        2                 Click here for a 20% banner
    a3      art        7                 Click here for a 70% banner

Tag would be:

    [banner weighted=1 category="tech"]

This will find *all* banners with a weight >= 1 where the C<category> field
is equal to C<tech>. The files will be made into the director C<tmp/Banners/tech>.

=item weighted 

To select weighted banners:

    [banner weighted=1]

This will find *all* banners with a weight >= 1. (Remember, integers
only.) The files will be made into the director C<tmp/Banners>.

    code    category   weight   rotate   banner
    t1      tech       1                 Tech banner 1
    t2      tech       2                 Tech banner 2
    t3      tech       7                 Tech banner 3
    a1      art        1                 Art banner 1
    a2      art        2                 Art banner 2
    a3      art        7                 Art banner 3

Each of the above with a weight of 7 will actually be displayed
35% of the time.

=item categorized, not rotating

    [banner category="tech"]

This is pretty much equivalent to:

    [data table=banner col=banner key=tech

The differences are:

    -- not selected if "rotate" field is blank
    -- if not selected, the default banner is displayed

Your C<banner> table would look like:

    code    category   weight   rotate   banner
    tech               0        0        Tech banner

You can put MiniVend tags in the category parameter if you
wish:

    [banner category="[value interest]"]

=item categorized and rotating

    [banner category="tech"]

The difference between this and above is the database.
Your C<banner> table would look like:

    code    category   weight   rotate   banner
    tech               0        1        Tech banner 1{or}Tech banner 2
    art                0        1        Art banner 1{or}Art banner 2

This would rotate between banner 1 and 2 for the category tech I<for each
user>. Banner 1 is always displayed first. The C<art> banner would never
be displayed unless you used the tag C<[banner category=art]>, of course.

You can put MiniVend tags in the category parameter if you
wish:

    [banner category="[value interest]"]

=item multi-level categorized

    [banner category="tech:hw"] or [banner category="tech:sw"]

If you have a colon-separated category, Minivend will select the most
specific ad available. If you C<banner> table looks like:

    code    category   weight   rotate   banner
    tech               0        1        Tech banner 1{or}Tech banner 2
    tech:hw            0        1        Hardware banner 1{or}HW banner 2
    tech:sw            0        1        Software banner 1{or}SW banner 2

This works the same as single-level categories, except that the
category tech:hw will select that banner. The category tech:sw will
select its own; but the category tech:html would just get the
"tech" banner.

Otherwise, it works just as in other categorized ads -- rotation will
work if set non-zero/non-blank, and it will be inactive if the rotate
field is blank. Each category rotates on its own.

=item ADVANCED

All parameters are optional since they are marked with an asterisk C<(*)>.

Tag syntax:

    [banner
        weighted=1*
        category=category*
        once=1*
        separator=sep*
        delimiter=delim*
        table=banner_table*
        a_field=banner_field*
        w_field=weight_field*
        r_field=rotate_field*
    ]

Defaults are blank except:

    table       banner    selects table used
    a_field     banner    selects field for banner text
    delimiter   {or}      delimiter for rotating ads
    r_field     rotate    rotate field
    separator   :         separator for multi-level categories
    w_field     weight    rotate field

=back

=head2 Tags for summarizing shopping basket/cart

The following elements are used to access common items
which need to be displayed on baskets and checkout pages.

B<* marks an optional parameter>

=over 4

=item [item-list cart*]

named attributes: C<[item-list name="cart"]>

Places an iterative list of the items in the specified shopping cart,
the main cart by default. See I<Item Lists> for a description.

=item [/item-list]

Terminates the C<[item-list]> tag.

=item [value field flag*]

Expands into the current value of the customer input field
named by field. If I<flag> is present, single and double
quotes will be escaped with a backslash; this allows reliable
SQL inserts. See the section on input fields for more
information.

=item [nitems cart*]

Expands into the total number of items ordered so far. Takes an
optional cart name as a parameter.

=item [subtotal]

Expands into the subtotal cost, exclusive of sales tax, of
all the items ordered so far.

=item [salestax cart*]

Expands into the sales tax on the subtotal of all the
items ordered so far. If there is no key field to derive the
proper percentage, such as state or zip code, it is set to 0.
See I<Sales Tax> for more information.

=item [shipping-description mode*]

named attributes: C<[shipping-description name="mode"]>

The text description of B<mode> -- the default is the 
shipping mode currently selected.

=item [shipping mode*]

named attributes: C<[shipping name="mode"]>

The shipping cost of the items in the basket via C<mode> -- the default
mode is the shipping mode currently selected in the C<mv_shipmode>
variable. See I<SHIPPING>.

=item [total-cost cart*]

Expands into the total cost of all the items in the current shopping cart,
including sales tax (if any).

=item [calc]

Starts a region where the arguments are calculated according to normal
arithmetic symbols. For instance:

    [calc] 2 + 2 [/calc]

will display:

    4

=item [/calc]

Terminates the calculated region.

The C<[calc]> tag is really the same as the C<[perl]> tag, except
that it doesn't accept arguments, is more efficient to parse, and
is interpolated at a higher precedence.

TIP: The C<[calc]> tag will remember variable values inside one page, so
you can do the equivalent of a memory store and memory recall for a loop.

=item [currency convert*]

named attributes: C<[currency convert=1*]>

When passed a value of a single number, formats it according to the
currency specification. For instance:

    [currency]4[/currency]

will display:

    4.00

Uses the I<Locale> and I<PriceCommas> settings as appropriate, and can
contain a C<[calc]> region. If the optional "convert" parameter is set,
it will convert according to PriceDivide> for the current locale. If
Locale is set to C<fr_FR>, and F<PriceDivide> for C<fr_FR> is 0.167,
the following sequence

    [currency convert=1] [calc] 500.00 + 1000.00 [/calc] [/currency]

will cause the number 8.982,04 to be displayed.

=item [/currency]

Terminates the currency region.

=item [cart name]

named attributes: C<[cart name="name"]>

Sets the name of the current shopping cart for display of shipping, price,
total, subtotal, and nitems tags. If you wish to use a different price
for the cart, all of the above except C<[shipping]> will reflect the normal
price field. You must emulate those operations with embedded Perl or the
C<[item-list]>, C<[calc]>, and C<[currency]> tags, or use the I<PriceAdjustment>
feature to set it.

=item [row nn]

named attributes: C<[row width="nn"]>

Formats text in tables. Intended for use in emailed reports or <PRE></PRE> HTML
areas. The parameter I<nn> gives the number of columns to use. Inside the
row tag, C<[col param=value ...]> tags may be used. 

=item [/row]

Terminates a C<[row nn]> element.

=item [col width=nn wrap=yes|no gutter=n align=left|right|input spacing=n]

Sets up a column for use in a C<[row]>. This parameter can only be contained
inside a C<[row nn] [/row]> tag pair. Any number of columns (that fit within
the size of the row) can be defined.

The parameters are:

    width=nn        The column width, I<including the gutter>. Must be
                    supplied, there is no default. A shorthand method
                    is to just supply the number as the I<first> parameter,
                    as in [col 20].
        
    gutter=n        The number of spaces used to separate the column (on
                    the right-hand side) from the next. Default is 2.
        
    spacing=n       The line spacing used for wrapped text. Default is 1,
                    or single-spaced.
        
    wrap=(yes|no)   Determines whether text that is greater in length than
                    the column width will be wrapped to the next line. Default
                    is I<yes>.
        
    align=(L|R|I)   Determines whether text is aligned to the left (the default),
                    the right, or in a way that might display an HTML text
                    input field correctly.

=item [/col]

Terminates the column field.

=back

=head2 Item Lists

Within any page, the C<[item-list cart*]> element shows a list of all the
items ordered by the customer so far. It works by repeating the source
between C<[item-list]> and C<[/item-list]> once for each item ordered.

NOTE: The special tags that reference item within the list are not normal
MiniVend tags, do not take named attributes, and cannot be contained in
an HTML tag (other than to substitute for one of its values or provide
a conditional container). They are interpreted only inside their
corresponding list container. Normal MiniVend tags can be interspersed,
though they will be interpreted I<after> all of the list-specific tags.

Between the item_list markers the following elements will return
information for the current item:

=over 4

=item [if-item-data table column]

If the database field C<column> in table I<table> is non-blank, the
following text up to the C<[/if-item-data]> tag is substituted. This can be
used to substitute IMG or other tags only if the corresponding source
item is present. Also accepts a C<[else]else text[/else]> pair for the
opposite condition.

NOTE: This tag does not nest with other C<[if-item-data ...]> tags.

=item [if-item-data ! table column]

Reverses sense for C<[if-item-data]>.

=item [/if-item-data]

Terminates an C<[if-item-data table column]> element.

=item [if-item-field fieldname]

If the products database field I<fieldname> is non-blank, the following
text up to the C<[/if-item-field]> tag is substituted. If you have more than
one products database table (see I<ProductFiles>), it will check
them in order until a matching key is found. This can be used to
substitute IMG or other tags only if the corresponding source
item is present. Also accepts a C<[else]else text[/else]> pair
for the opposite condition.

NOTE: This tag does not nest with other C<[if-item-field ...]> tags.

=item [if-item-field ! fieldname]

Reverses sense for C<[if-item-field]>.

=item [/if-item-field]

Terminates an C<[if-item-field fieldname]> element.

=item [item-accessories attribute*, type*, field*, database*, name*]

Evaluates to the value of the Accessories database entry for the item.
If passed any of the optional arguments, initiates special processing
of item attributes based on entries in the product database.

=item [item-alternate N] DIVISIBLE [else] NOT DIVISIBLE [/else][/item-alternate]

Set up an alternation sequence. If the item-increment is divisible by C<N>,
the text will be displayed. If an C<[else]NOT DIVISIBLE TEXT[/else]> is present,
then the NOT DIVISIBLE TEXT will be displayed.

For example:

    [item-alternate 2]EVEN[else]ODD[/else][/item-alternate]
    [item-alternate 3]BY 3[else]NOT by 3[/else][/item-alternate]

=item [/item-alternate]

Terminate the alternation area.

=item [item-code]

Evaluates to the product code for the current item.

=item [item-data database fieldname]

Evaluates to the field name I<fieldname> in the arbitrary database
table I<database>, for the current item.

=item [item-description]

Evaluates to the product description (from the products file)
for the current item.

=item [item-field fieldname]

The [item-field ...] tag is special in that it looks in any of the tables
defined as C<ProductFiles>, in that order, for the data, returning
the value only if that key is defined. In most catalogs, where C<ProductFiles> is
not defined (i.e. the demo), C<[item-field title]> is equivalent to
C<[item-data products title]>.

Evaluates to the field name I<fieldname> in the products database,
for the current item. If the item is not found in the first of the
I<ProductFiles>, all will be searched in sequence.

=item [item-increment]

Evaluates to the number of the item in the match list. Used
for numbering search matches or order items in the list.

=item [item-last]tags[/item-last]

Evaluates the output of the MiniVend tags encased inside the tags,
and if it evaluates to a numerical non-zero number (i.e. 1, 23, or -1)
then the list iteration will terminate. If the evaluated number is
B<negative>, then the item itself will be skipped. If the evaluated
number is B<positive>, then the item itself will be shown but will be
last on the list.

      [item-last][calc]
        return -1 if '[item-field weight]' eq '';
        return 1 if '[item-field weight]' < 1;
        return 0;
        [/calc][/item-last]

If this is contained in your C<[item-list]> (or C<[search-list]> or
flypage) and the weight field is empty, then a numerical C<-1> will
be output from the C<[calc][/calc]> tags; the list will end and the item
will B<not> be shown. If the product's weight field is less than 1,
a numerical 1 is output.  The item will be shown, but will be the last
item shown. (If it is an C<[item-list]>, any price for the item will
still be added to the subtotal.) NOTE: no HTML style.

=item [item-modifier attribute]

Evaluates to the modifier value of C<attribute> for the current item.

=item [item-next]tags[/item_next]

Evaluates the output of the MiniVend tags encased inside, and
if it evaluates to a numerical non-zero number (i.e. 1, 23, or -1) then
the item will be skipped with no output. Example:

      [item-next][calc][item-field weight] < 1[/calc][/item-next]

If this is contained in your C<[item-list]> (or C<[search-list]> or flypage)
and the product's weight field is less than 1, then a numerical C<1> will
be output from the [calc][/calc] operation. The item will not be shown. (If
it is an C<[item-list]>, any price for the item will still be added to the
subtotal.)

=item [item-price n* noformat*]

Evaluates to the price for quantity C<n> (from the products file)
of the current item, with currency formatting. If the optional "noformat"
is set, then currency formatting will not be applied.

=item [discount-price n* noformat*]

Evaluates to the discount price for quantity C<n> (from the products file)
of the current item, with currency formatting. If the optional "noformat"
is set, then currency formatting will not be applied. Returns regular
price if not discounted.

=item [item-discount]

Returns the difference between the regular price and the discounted price.

=item [item-quantity]

Evaluates to the quantity ordered for the current item.

=item [item-subtotal]

Evaluates to the subtotal (quantity * price) for the current item.
Quantity price breaks are taken into account.

=item [modifier-name attribute]

Evaluates to the name to give an input box in which the
customer can specify the modifier to the ordered item.

=item [quantity-name]

Evaluates to the name to give an input box in which the
customer can enter the quantity to order.

=back

=head2 Embedded Perl Code

Perl code can be directly embedded in MiniVend pages. The code
is specified as:

    [perl]
        $name    = $Values->{name};
        $browser = $Session->{browser};
        return "Hi, $name! How do you like your $browser?";
    [/perl]

ASP syntax can be used with:

    [mvasp]
        <%
        $name    = $Values->{name};
        $browser = $Session->{browser};
        %>
        Hi, <%= $name %>!
        <%
            HTML "How do you like your $browser?";
        %>
    [/perl]

The two snippets above are essentially equivalent.

The C<[perl]> tag enforces I<Safe.pm> checking, and many standard Perl
operators are not available. This is a C<good> thing, as you would not
want users having access to all files and programs on the system with
the MiniVend daemon's permissions! See I<GlobalSub> and I<UserTag>
for ways to make external files and programs available to MiniVend.

named attributes:

    [perl tables="tables-to-open"* subs=1 global=1* no_return=1*]

Required attributes: none

Attribute information:

=over 4

=item global

When set to C<1>, and when the catalog is allowed to use global
permissions via the C<minivend.cfg> directive C<AllowGlobal>, Safe.pm
checking is turned off. In this case, the embedded Perl code can do anything
that the user ID running it can! This is not recommended when the same
MiniVend server runs for different companies/user ids. Be careful! Security
is your responsiblity and should be your concern.

=item subs

When set to C<1>, any C<GlobalSub> routines can be accessed by name.

=item tables

Opens specified MiniVend database tables, preparing them for access
inside the Perl code. 

When running under C<Safe>, some database operations are restricted
due to inability to create objects. For SQL there is nothing that can
be done -- you must have C<global> access to use data from SQL tables.
But for MiniVend databases, you can access them as long as you pre-open
them by using an item first.

Example:

    [perl tables="products pricing"]
        $key = $Values->{sku};
        $title = $Tag->data('products', 'title', $key):
        return "You looked up the title $title";
    [/perl]

If the tables=products> was not specified, there would be a syntax
error trap from C<Safe.pm>.

=back

Any MiniVend tag (except ones using SQL) can be accessed
via the $Tag object. If you need to use SQL queries inside a Perl
element, you will have to have C<AllowGlobal> permissions and set
the C<global=1> parameter.

Examples:

    # If the item might contain a single quote
    [perl]
    $comments = $Value->{comments};
    [/perl]

B<IMPORTANT NOTE:> Global subroutines are not subject to the stringent
security checking of the I<Safe> module, so almost anything goes
there. The subroutine will be able to modify any variable in MiniVend,
and will be able to write to read and write any file that the MiniVend
daemon has permission to write. Though this gives great power, it should
be used with caution. Careful! They are defined in the main minivend.cfg
file, so should be safe from individual users in a multi-catalog system.

Global subroutines are defined in I<minivend.cfg> with the
I<GlobalSub> directive, or in user catalogs which have been
enabled via I<AllowGlobal>. Global subroutines are much faster
than the others as they are pre-compiled. (Faster still are I<UserTag>
definitions.)

Catalog subroutines are defined in I<catalog.cfg>, with
the I<Sub> directive. They are subject to the stringent I<Safe.pm>
security restrictions that are controlled by I<SafeUntrap>. 

The code can be as complex as desired, but cannot use any operators
that modify the file system or use "unsafe" operations like "system",
"exec", or backticks. These constraints are enforced with the default
permissions of the standard Perl module I<Safe> -- operations may
be untrapped on a system-wide basis with the I<SafeUntrap> directive.

The result of the tag will be the result of the last expression
evaluated, just as in a subroutine. If there is a syntax error
or other problem with the code, there will be no output.

Here is a simple one which does the equivalent of the classic
hello.pl program:

    [perl] my $tmp = "Hello, world!"; $tmp; [/perl]

Of course you wouldn't need to set the variable -- it is just there
to show the capability.

To echo the user's browser, but within some HTML tags:

    [perl]
    my $html = '<H5>';
    $html .= $Session->{'browser'};
    $html .= '</H5>';
    $html;
    [/perl]

To show the user their name, and the current time:

    [perl arg=values]

    my $string = "Hi, " . $Values->{'name'} ". The time is now ";
    $string .= $Tag->mvtime();
    $string;

    [/perl]


=head2 ASP-like Perl

MiniVend supports an ASP-like syntax using the C<mvasp> tag.

    [mvasp]
    <HTML><BODY> 
        This is HTML.<BR>

    <% HTML "This is code<BR>"; %>
        More HTML.<BR>
    <% $Document->write("Code again.<BR>") %>
    [/mvasp]

If no closing C<[/mvasp]> tag is present, the remainder of the
page will be seen as ASP.

ASP is simple. Anything between <% and %> is code, and the string %> is
not allowed anywhere inside. Anything not between those anchors is 
plain HTML that will be placed unchanged on the page. MiniVend variables
and [L][/L] and [LC][/LC] areas will still be inserted, but any MiniVend
tags will not be.

There is a shorthand <% = $foo %>, which is exactly equivalent
to <% $Document->write($foo); %> or <% HTML $foo; %>

    [mvasp]
    <HTML><BODY> 
        This is HTML.<BR>
        [value name] will show up as &#91;value name].<BR>

        &#95_VARIABLE__ value is equal to: __VARIABLE__

    <% = "This is code<BR>" %>

The __VARIABLE__ will be replaced by the value of C<Variable VARIABLE>, but
[value name] will be shown unchanged.

IMPORTANT NOTE: If you make use of the SQL::Statement module, your catalog
must be set to AllowGlobal in C<minivend.cfg>. It will not work in "Safe"
mode due to limitations of object creation in Safe. Also, you must have
the Safe::Hole module installed to have SQL databases work in Safe mode.

=head1 MiniVend Perl Objects

MiniVend gives ready access to all objects associated with the 
catalog and the user settings, with opcode restrictions based on
the standard Perl module C<Safe.pm>.

There are some things you will need to know about programming
with Minivend. 

Under C<Safe>, certain things cannot be used. For instance, this
is not legal when running Safe:

    $variable = `cat file/contents`;

The backtick operator violates a number of the default Safe op
code restrictions. You can not do direct file opens, either:

    open(SOMETHING, "something.txt") 
        or die;

This will also cause a trap, and the code will fail to compile.

You can, however, use the Minivend equivalent routines:

    # This will work if your administrator doesn't have NoAbsolute
    # set
    $users = $Tag->file('/home/you/list');

    # This will always work, file names are based in the 
    # catalog directory
    $users = $Tag->file('userlist');

The standard objects are:

=over 2

=item $CGI

This is a hash reference to C<%CGI::values>, the value of user variables
as submitted in the current page/form. To get the value of a variable
submitted as

    <INPUT TYPE=hidden NAME=foo VALUE=bar>

use 

    <% $Document->write("Value of foo is $CGI->{'foo'}"); %>

Remember, multiple settings of the same variable are separated by
a NULL character. To get the array value, use $CGI_array.

=item $CGI_array

This is a hash reference to C<%CGI::values_array>, arrays containing the value
or values of user variables as submitted in the current page/form. To get the
value of a variable submitted as

    <INPUT TYPE=hidden NAME=foo VALUE=bar>
    <INPUT TYPE=hidden NAME=foo VALUE=baz>

use 

    <% = "The values of foo are", join (' and ', @{$CGI_array->{'foo'}}) %>

Remember, multiple settings of the same variable are separated by
a NULL character. To get the array value, use $CGI_array.

=item $Carts

A reference to the shopping cart hash $Vend::Session->{carts}. The
normal default cart is "main". An alias for that is $Items in the
normal course of events.

Shopping carts are an array of hash references. Here is an example
of a session cart array containing a C<main> and a C<layaway> cart.

    {
        'main' => [ 
                    {
                        'code' => '00-0011',
                        'mv_ib' => 'products',
                        'quantity' => 1,
                        'size' => undef,
                        'color' => undef
                    },
                    {
                        'code' => '99-102',
                        'mv_ib' => 'products',
                        'quantity' => 2,
                        'size' => 'L',
                        'color' => 'BLUE'
                    }
                ],
        'layaway' => [ 
                    {
                        'code' => '00-341',
                        'mv_ib' => 'products',
                        'quantity' => 1,
                        'size' => undef,
                        'color' => undef
                    }
                ]
    }

In this cart array $Carts->{main}[1]{'code'} is equal to C<99-102>. In
the normal course of events, it would be equivalent to $Items->[1]{'code'}.

=item $Config

A reference to the $Vend::Cfg array. Normally you should use this with
a liberal dose of the MiniVend source code, but for simple things you can
use something like:

    # Turn off CyberCash for this user
    $Config->{CyberCash} = 0;

Changes are not persistent except when running in the foreground (Debug mode
or on Windows).

=item %DB

A hash of databases that you shared with the C<[mvasp tables="foo"]> parameter
to the tag call. Once you share the database, it is opened and can
be accessed by any of its methods.

ONCE AGAIN, this will not work with SQL unless AllowGlobal is set for
your catalog.

To get a reference to a particular table, specify its hash element:

    $ref = $Db{products};

The methods available:

    # access an element of the table
    $field = $ref->field($key, $column);

    # set an element of the table
    $ref->set_field($key, $column_name, $value);

    # atomic increment of an element of the table
    $ref->inc_field($key, $column_name, 1);

    # see if element of the table is numeric
    $is_numeric = $ref->numeric($column_name);

    # Quote for SQL query purposes
    $quoted = $ref->quote($value, $column_name);

    # Check configuration of the database
    $delimiter = $ref->config('DELIMITER');

    # Find the names of the columns (not including the key)
    @columns = $ref->columns();
    # Insert the key column name
    unshift @columns, $ref->config('KEY');

    # See if a column is in the table
    $is_a_column = defined $ref->test_column($column_name);

    # See if a row is in the table
    $is_present = $ref->record_exists($key);

    # Create a subroutine to return a single column from the table
    $sub = $ref->field_accessor($column);
    for (@keys) {
        push @values, $sub->($key);
    }

    # Create a subroutine to set a single column in the database
    $sub = $ref->field_settor($column);
    for (@keys) {
        $sub->($key, $value);
    }

    # Create a subroutine to set a slice of the database
    $sub = $ref->row_settor(@columns);
    for (@keys) {
        $sub->($key, @values);
    }

    # Retrurn a complete array of the database (minus the key)
    @values = $ref->row($key);

    # Retrurn a complete hash of the database row (minus the key)
    $hashref = $ref->row_hash($key);

    # Delete a record/row from the table
    $ref->delete_record($key);

=item %SQL

A hash of SQL databases that you shared with the C<[mvasp tables="foo"]>
parameter to the tag call. It returns the DBI database handle, so
you can do things like:

  <%
    my $dbh = $Sql{products}
        or return HTML "Database not shared.";
    my $sth = $dbh->prepare('select * from products')
        or return HTML "Couldn't open database.";
    $sth->execute();
    my @record;
    while(@record = $sth->fetchrow()) {
        foo();
    }
    $sth = $dbh->prepare('select * from othertable')
        or return HTML "Couldn't open database.";
    $sth->execute();
    while(@record = $sth->fetchrow()) {
        bar();
    }
  %>

ONCE AGAIN, this will not work with unless AllowGlobal is set for
your catalog. And full "use strict" checking is in force.

=item $DbSearch

A search object which will search a database without using the text file.
It is the same as MiniVend's C<db> searchtype. Options are specified
in a hash and passed to the object. All multiple-field options should
be passed as array references.

Before using the $DbSearch object, you must tell it which table
to search -- to use the table C<foo> it must have been shared
with C<[mvasp foo]>.

There are three search methods -- C<array>, C<hash>, and C<list>.

    array    Returns a reference to an array of arrays (best)
    hash     Returns a reference to an array of hashes (slower)
    list     Returns a reference to an array of tab-delimited lines

Example:

    $DbSearch->{table} = $Db{foo};

    $search = {

            mv_searchspec => 'Mona Lisa',
            mv_search_field => [ 'title', 'artist', 'price' ],
            mv_return_fields    => ['

        }; 
  
    my $ary = $DbSearch->array($search);
  
    if(! scalar @$ary) {
        return HTML "No match.\n";
    }
  
    for(@$ary) {


=item $Document

This is an object that has several routines associated with it.

 HTML $foo;                     # Append $foo to the write buffer array
 $Document->write($foo);        # object call to append $foo to the write
                                # buffer array
 $Document->insert($foo);       # Insert $foo to front of write buffer array
 $Document->header($foo, $opt); # Append $foo to page header
 $Document->send();             # Send write buffer array to output, done
                                # automatically upon end of ASP, clears buffer
                                # and invalidates $Document->header()
 $Document->hot(1);             # Cause writes to send immediately
 $Document->hot(0);             # Stop immediate send
 @ary = $Document->review();    # Place contents of write buffer in @ary
 $Document->replace(@ary)       # Replace contents of write buffer with @ary
 $ary_ref = $Document->ref();   # Return ref to output buffer

=item $Document->write($foo)

Write $foo to the page in a buffered fashion. The buffer is an array that
is the results of all previous C<$Document->>C<write()> operations. If
$Document->hot(1) has been set, the output immediately goes to the user.

=item $Document->insert($foo)

Insert $foo to the page buffer.

    $Document->write("23");
    $Document->insert("1");
    $Document->send();

will output "123", while

    $Document->write("23");
    $Document->write("1");
    $Document->send();

will output "231".

=item $Document->header($foo, $opt)

Add the header line $foo to the HTTP header. You can use this to
change the page content type, cache options, or other attributes.

The below code changes the content type (MIME type) to text/plain:

    $Document->header("Content-type: text/plain");

There is an option hash that can be sent, with the only valid value
being "replace". The below code scrubs all previous header lines:

    $Document->header("Content-type: text/plain", { replace => 1 } );

Once you have sent output with $Document->send(), this can no longer
be done.

=item $Document->hot($foo)

If the value of $foo is true (in a Perl sense), then all $Document->write()
operations will be immediately sent until a $Document->hot(0) is executed.

=item $Document->send()

Causes the document write buffer to be sent to the browser and empties
the buffer. Any further $Document->header() calls will be ignored.

Can be used to implement non-parsed-header operation.

=item $Document->review()

Returns the value of the write buffer.

    @ary = $Document->review();

=item $Document->replace(@new)

Completely replaces the write buffer with the arguments.

=item $Document->ref()

Returns a reference to the write buffer.

    # Remove the first item in the write buffer
    my $ary_ref = $Document->ref();
    shift @$ary_ref;

=item HTML

Writes a string (or list of strings) to the write buffer array. The
call

    HTML $foo, $bar;

is exactly equivalent to

    $Document->write($foo, $bar);

Honors the $Document->hot() setting.

=item $Items

A reference to the I<current> shopping cart. Unless you have used a MiniVend
C<[cart ...]> tag, it is normally the same as $Carts->{main}.

=item $Scratch

A reference to the scratch values ala C<[scratch foo]>.

    <% $Scratch->{foo} = 'bar'; %>

is equivalent to:

    [set foo]bar[/set]

=item $Session

A reference to the session values ala C<[data session username]>.

    <%
        my $out = $Session->{browser};
        $Document->write($out);
    %>

is equivalent to:

    [data session browser]

You can also set values. If you wanted to change the value of 
C<[data session source]>, for example, you could do:

    <%
        $Session->{source} = 'New_partner';
    %>

=item $Tag

Using the $Tag object, you can access any MiniVend tag including
user-defined tags.

B<IMPORTANT NOTE:> If the tag will access a database that has
not been previously opened, you must pass in the table name in
the ASP call, i.e.:

    # HTML style
    <HTML MV="mvasp" MV.TABLES="products pricing">
     
      or
     
    # Named parameters
    [mvasp tables="products pricing"] 
     
      or
     
    # Positional parameters
    [mvasp products pricing] 

Any tag can be called.

    <%
        my $user = $Session->{username};
        my $name_from_db = $Tag->data('userdb', 'name', $user );
        $Document->write($name_from_db);
    %>

is the same as:

    [data table=userdb column=name key="[data session username]"]

If the tag has a dash (C<->) in it, use an underscore instead:

    # WRONG!!!
    $Tag->shipping-desc('upsg');
    # Right
    $Tag->shipping_desc('upsg');

There are two ways of specifying parameters. You can either use
the positional parameters as documented (for an authoritative
look at the parameters, trace the %Routine value in Vend::Parse), or
you can specify it all with an option hash parameter names as in
any named parameters as you would specify in a MiniVend tag. The calls

    $Tag->data('products', 'title', '00-0011');

and

    my $opt = {     
                    table   => 'products',
                    column  => 'title',
                    key     => '00-0011',
                };

    $Tag->data( $opt );

are equivalent for the C<data> tag.

If you are using the option hash method, and the tag has container text, you
can either specify it in the hash parameter C<body> or add it as the next
argument. The two calls:

    $Tag->item_list( { 
                        'body' => "[item-code] [item-field title]",
                    });

and 

    $Tag->item_list( { }, "[item-code] [item-field title]")

are equivalent.
    
                        
    
                        

Parameter names are ALWAYS lower case.


=item $Values

A reference to the user form values ala C<[value foo]>.

    <% $Document->write($Values->{foo}); %>

is equivalent to:

    [value foo]

=item &Log

Send a message to the error log (same as ::logError in GlobalSub or global UserTag).

    <% 
        Log("error log entry");
    %>

It prepends the normal timestamp with user and page information. To supress
that information, begin the message with a backslash (C<\>).

    <% 
        Log("\\error log entry without timestamp");
        Log('\another error log entry without timestamp');
        Log("error log entry with timestamp");
    %>

=back

=head2 On-the-fly Catalog Pages

If an item is displayed on the search list (or order list) and there is
a link to a special page keyed on the item, MiniVend will attempt to
build the page "on the fly". It will look for the special page
I<flypage.html>, which is used as a template for building the page. If
C<[item-field fieldname]>, C<[item-price]>, (etc.) elements are used on the page,
quite complex and information-packed pages can be built. The
C<[if-item-field fieldname]> HTML C<[/if-item-field]> pair can be used to insert
HTML only if there is a non-blank value in a particular field.

IMPORTANT NOTE: Because the tags are substituted globally on the page,
you cannot use C<[item-*]> tags on the default on-the-fly page.
If you want to use a [search-region] or [item-list] tag, change the
default with the prefix parameter. Example:

    [item-list prefix=cart]
    [cart-code] -- title=[cart-data products title]
    [/item-list]

If you want to have an on-the-fly page mixed in reliably, use the idiom
C<[fly-list prefix=fly code="[data session arg]"]> C<[/flylist]> pair.

=over 4

=item [fly-list code="product_code" base="table"] ... [/fly-list]

Other parameters:

    prefix=label     Allows [label-code], [label-description]

Defines an area in a random page which performs the flypage lookup
function, implementing the tags below.

   [fly-list code="[data session arg]"]
    (contents of flypage.html)
   [/fly-list]

If you place the above around the contents of the demo flypage,
in a file named C<flypage2.html>, it will make these two calls
display identical pages:

    [page 00-0011] One way to display the Mona Lisa [/page]
    [page flypage2 00-0011] Another way to display the Mona Lisa [/page]

=back

If the directive I<PageSelectField> is set to a valid product database
field which contains a valid MiniVend page name (relative to the catalog
pages directory, without the .html suffix) it will be used to build the
on-the-fly page.

Active tags in their order of interpolation:

 [if-item-field field]    Tests for a non-empty, non-zero value in B<field>
 [if-item-data db field]  Tests for a non-empty, non-zero B<field> in B<db>
 [item-code]              Product code of the displayed item
 [item-accessories args]  Accessory information (see I<accessories>)
 [item-description]       Description field information
 [item-price quantity*]   Product price (at B<quantity>)
 [item-field field]       Product database B<field>
 [item-data db field]     Database B<db> entry for B<field>

=head2 Special Pages

A number of HTML pages are special for MiniVend operation. Typically they
are used to transmit error messages, status of search or order operations, and
other out of boundary conditions.

B<NOTE:> The distributed demo does not use all of the default values.

The names of these pages can be set with the I<SpecialPage>
directive. The standard pages and their default locations:

=over 4 

=item canceled (special_pages/canceled.html)

The page displayed by MiniVend when an order has been canceled by
the user.

=item catalog (special_pages/catalog.html)

The main catalog page presented by MiniVend when another page is
not specified.

=item failed (special_pages/failed.html)

If the sendmail program could not be invoked to email the
completed order, the failed.html page is displayed. (Sadly we
don't know if the email was successfully delivered).

=item flypage (special_pages/flypage.html)

If the catalog page for an item was not found when its C<[item-link]>
is clicked, this page is used as a template to build an on-the-fly
page. See I<On-the-fly Catalog Pages>.

=item interact (special_pages/interact.html)

Displayed if an unexpected response was received from the
browser, such as not getting expected fields from submitting a
form. This would probably happen from typos in the html pages,
but could be a browser bug.

=item missing (special_pages/missing.html)

This page is displayed if the URL from the browser specifies a
page that does not have a matching .html file in the pages
directory. This can happen if the customer saved a bookmark to
a page that was later removed from the database, for example,
or if there is a defect in the code. 

Essentially this is the same as a 404 error in HTTP. If you
want to actually display a 404 error, just put this in
special_pages/missing.html:

    [tag op=header]Status: 404 missing[/tag]

=item nomatch (special_pages/nomatch.html)

This page is displayed if the search engine is used, but there
is no match for the search specification and no C<[no-match]>
region found on the search page.

=item noproduct (special_pages/noproduct.html)

This page is displayed if the URL from the browser specifies
the ordering of a product code which is not in the products
file.

=item order (special_pages/order.html)

This page is displayed when the customer orders an item. It can contain
any or all of the customer-entered values, but is commonly used as a
status display (or "shopping basket").

It is normal to set this page to something in your user
page space:

    SpecialPage   order   ord/basket

=item search (special_pages/search.html)

Contains the default output page for the search engine results. Also
required is an input page, which can be the same as search.html or an
additional page.

By convention MiniVend defines this as the page C<results>.

    SpecialPage   search   results

=item violation (pages/violation.html)

Displayed if a security violation is noted, such as an attempt to
access a page denied by an C<access_gate>. See I<UserDB>.

=back

=head2 Checking Page HTML

MiniVend allows you to debug your page HTML with an external page
checking program.  Because leaving this enabled on a production system
is potentially a very bad performance degradation, the program is set
in a the global configuration file with the I<CheckHTML> directive.

To check a page for validity, set the global directive I<CheckHTML> 
to the name of the program (don't do any output redirection). A good
choice is the freely available program I<weblint> -- it would be
set in I<minivend.cfg> with:

    CheckHTML  /usr/local/bin/weblint -s -

Of course you must restart the server for it to be recognized.
The full path to the program should be used -- if you have trouble,
check it from the command line (as you should with all external programs
called by MiniVend).

Insert C<[tag flag checkhtml][/tag]> at the top or bottom
of pages you want to check, and the output of your checker
should be appended to the browser output as a comment, visible
if you view the page or frame source.

To do this only at times, use a I<Variable> setting:

    Variable  CHECK_HTML    [tag flag checkhtml][/tag]

and place __CHECK_HTML__ in your pages.  You can then set
the Variable to the empty string if you wish to disable it.

=head1 FORMS AND MINIVEND

MiniVend uses HTML forms for many of its functions, including
ordering, searching, updating account information, and maintaining databases.
Order operations possibly include ordering an item, selecting item
size or other attributes, and reading user information for payment
and shipment. Search operations may also be triggered by a form.

MiniVend supports file upload with the C<multipart/form-data>
type. The file is placed in memory and discarded if not accessed with
the C<[value-extended name=filevar file_contents=1]> tag or written
with C<[value-extended name=filevar outfile=your_file_name]>. See
I<Extended Value Access and File Upload>.

=head2 Special Form Fields

MiniVend treats some form fields specially, to link to the search engine
and provide more control over user presentation.  It has a number of
predefined variables, most of whose names are prefixed with C<mv_> to
prevent name clashes with your variables. It also uses a few variables
which are postfixed with integer digits -- those are used to provide
control in its iterating lists.

Most of these special fields begin with C<mv_>, and include:

(O = order, S = search, C = control, A = all, X in scratch space)

 Name               scan Type  Description

 mv_all_chars         ac  S   Turns on punctuation matching
 mv_arg[0-9]+             A   Parameters for mv_subroutine (mv_arg0,mv_arg1,...)
 mv_base_directory    bd  S   Sets base directory for search file names
 mv_begin_string      bs  S   Pattern must match beginning of field
 mv_case              cs  S   Turns on case sensitivity
 mv_cartname              O   Sets the shopping cart name
 mv_cache_params          S   Determines caching of searches
 mv_change_frame          A   Any form, changes frame target of form output
 mv_check                 A   Any form, sets multiple user variables after update
 mv_checkout              O   Sets the checkout page
 mv_click                 A   Any form, sets multiple form variables before update
 mv_click                 XA  Default mv_click routine, click is mv_click_arg
 mv_click <name>          XA  Routine for a click <name>, sends click as arg
 mv_click_arg             XA  Argument name in scratch space
 mv_coordinate        co  S   Enables field/spec matching coordination
 mv_column_op         op  S   Operation for coordinated search
 mv_credit_card*          O   Discussed in order security (some are read-only)
 mv_delay_page        dp  S   Delay search until after inital page display
 mv_dict_end          de  S   Upper bound for binary search
 mv_dict_fold         df  S   Non-case sensitive binary search
 mv_dict_limit        di  S   Sets upper bound based on character position
 mv_dict_look         dl  S   Search specification for binary search
 mv_dict_order        do  S   Sets dictionary order mode
 mv_doit                  A   Sets default action
 mv_email                 O   Reply-to address for orders
 mv_exact_match       em  S   Sets word-matching mode
 mv_failpage          fp  O,S Sets page to display on failed order check/search
 mv_field_file        ff  S   Sets file to find field names for Glimpse
 mv_field_names       fn  S   Sets field names for search, starting at 1
 mv_first_match       fm  S   Start displaying search at specified match
 mv_head_skip         hs  S   Sets skipping of header line(s) in index
 mv_index_delim       id  S   Delimter for search fields (TAB default)
 mv_matchlimit        ml  S   Sets match page size
 mv_max_matches       mm  S   Sets maximum match return (only for Glimpse)
 mv_min_string        ms  S   Sets minimum search spec size
 mv_negate            ne  S   Records NOT matching will be found
 mv_nextpage          np  A   Sets next page user will go to
 mv_numeric           nu  S   Comparision numeric in coordinated search
 mv_order_group           O   Allows grouping of master item/sub item
 mv_order_item            O   Causes the order of an item
 mv_order_number          O   Order number of the last order (read-only)
 mv_order_quantity        O   Sets the quantity of an ordered item
 mv_order_profile         O   Selects the order check profile 
 mv_order_receipt         O   Sets the receipt displayed
 mv_order_report          O   Sets the order report sent
 mv_order_subject         O   Sets the subject line of order email
 mv_orsearch          os  S   Selects AND/OR of search words
 mv_profile           mp  S   Selects search profile
 mv_range_alpha       rg  S   Sets alphanumeric range searching
 mv_range_look        rl  S   Sets the field to do a range check on
 mv_range_max         rx  S   Upper bound of range check
 mv_range_min         rm  S   Lower bound of range check
 mv_record_delim      dr  S   Search index record delimiter
 mv_return_all        ra  S   Return all lines found (subject to range search)
 mv_return_delim      rd  S   Return record delimiter
 mv_return_fields     rf  S   Fields to return on a search
 mv_return_file_name  rn  S   Set return of file name for searches
 mv_return_spec       rs  S   Return the search string as the only result
 mv_save_session          C   Set to non-zero to prevent expiration of user session
 mv_search_field      sf  S   Sets the fields to be searched
 mv_search_file       fi  S   Sets the file(s) to be searched
 mv_search_line_return lr S   Each line is a return code (loop search)
 mv_search_match_count    S   Returns the number of matches found (read-only)
 mv_search_page       sp  S   Sets the page for search display
 mv_searchspec        se  S   Search specification
 mv_searchtype        st  S   Sets search type (text, glimpse, db or sql)
 mv_separate_items        O   Sets separate order lines (one per item ordered)
 mv_session_id        id  A   Suggests user session id (overridden by cookie)
 mv_shipmode              O   Sets shipping mode for custom shipping
 mv_sort_field        tf  S   Field(s) to sort on
 mv_sort_option       to  S   Options for sort
 mv_spelling_errors   er  S   Number of spelling errors for Glimpse
 mv_substring_match   su  S   Turns off word-matching mode
 mv_successpage           O   Page to display on successful order check
 mv_todo                  A   Common to all forms, sets form action
 mv_todo.map              A   Contains form imagemap
 mv_todo.checkout.x       O   Causes checkout action on click of image
 mv_todo.return.x         O   Causes return action on click of image
 mv_todo.submit.x         O   Causes submit action on click of image
 mv_todo.x                A   Set by form imagemap
 mv_todo.y                A   Set by form imagemap
 mv_unique            un  S   Return unique search results only
 mv_value             va  S   Sets value on one-click search (va=var=value)

=head2 Form Actions

MiniVend form processing is based on an C<action> and a C<todo>. The 
predefined actions at the first level are:

    process       process a todo
    search        form-based search
    scan          path-based search
    order         order an item
    minimate      get access to a database via MiniMate

You can define any action you desire with C<ActionMap>.

The C<process> action has a second C<todo> level called with C<mv_todo>
or C<mv_doit>. The C<mv_todo> takes preference over C<mv_doit>, which 
can be used to set a default if no C<mv_todo> is set.

The action can be specified either with:

=over 4

=item page name

Calling the page "search" will cause the search action, C<process>
will cause a form process action, etc. Examples:

    <FORM ACTION="/cgi-bin/simple/search" METHOD=POST>
    <INPUT NAME=mv_searchspec>
    </FORM>

The above is a complete search in MiniVend -- it causes a simple text
search of the default products database(s). Normally you don't use
hard-coded paths, but use a minivend tag to specify it for portability:

    <FORM ACTION="[area search]" METHOD=POST>
    <INPUT NAME=mv_searchspec>
    </FORM>

You will often see the tag C<[process]> in MiniVend forms. The above
can be called equivalently with:

    <FORM ACTION="[process]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_todo VALUE=search>
    <INPUT NAME=mv_searchspec>
    </FORM>

=item mv_action

Setting the special variable C<mv_action> causes the page name to
be ignored as the action source. The above forms can use this as
a synonym:

    <FORM ACTION="[area foo]" METHOD=post>
    <INPUT TYPE=hidden NAME=mv_action VALUE=search>
    <INPUT NAME=mv_searchspec>
    </FORM>

The page name will be used to set C<mv_nextpage> if it is not
otherwise defined; if C<mv_nextpage> is present in the form it
will be ignored. 

=back

The second level C<todo> for the C<process> action has these
defined by default:

    search   Trigger a search
    submit   submit a form for validation (and possibly a final order)
    go       Go to C<mv_nextpage>
    return   Go to C<mv_nextpage>
    set      Update a database table
    refresh  Go to C<mv_orderpage|mv_nextpage> and check for
             ordered items
    cancel   Erase the user session

If you define a page name as an action with C<ActionMap>, or use 
of MiniVend's predefined action C<process>, it will cause form processing.
first level is setting the special page name C<process>, or speciis set to do a form C<process>, the for
MiniVend form can be used for any number of actions. The actions
are mapped by the I<ActionMap> directive in the catalog configuration
file, and are selected on the form with either the F<mv_todo> or F<mv_doit>
variables.

To set a default action for a C<process> form, set the variable C<mv_doit> as
a hidden variable:

    <INPUT TYPE=hidden NAME=mv_doit VALUE=refresh>

When the F<mv_todo> value is not found, the I<refresh> action defined
in F<mv_doit> will be used instead.

More on the defined actions:

=over 4

=item cancel

All user information is erased, and the shopping cart is emptied. The
user is then sent to mv_nextpage.

=item refresh

Checks for newly-ordered items in C<mv_order_item>, looking for on-the-fly
items if that is defined, then updates the shopping cart with any changed
quantities or options.  Finally updates the user variables and returns
to the page defined in mv_orderpage or mv_nextpage (in that order of
preference).

=item return

Updates the user variables and returns to the page defined
in mv_nextpage.

=item search

The shopping cart and user variables are updated, then the form
variables are interpreted and the search specification contained
therein is dispatched to the search engine -- results are returned
on the defined search page (set by F<mv_search_page> or the search
page directives).

=item submit

Submit the form for order processing. If no order profile is defined
with the C<mv_order_profile> variable, the order is checked to see
if the current cart contains any items and the order is submitted.

If there is an order profile defined, the form will be checked against
the definition in the order profile and submitted if the pragma &final
is set to B<yes>. If &final is set to B<no> (the default), and the
check succeeds, the user will be routed to the MiniVend page defined in
mv_successpage, or mv_nextpage. Finally, if the check fails, the user
will be routed to mv_failpage or mv_nextpage in that order.

=back

=head2 One-click Multiple Variables

MiniVend can set multiple variables with a single button or form
control. You first define the variable set (or profile, as in
search and order profiles) inside a scratch variable:

  [set Search by Category]
  mv_search_field=category
  mv_search_file=categories
  mv_todo=search
  [/set]

The special variable C<mv_click> sets variables just as if they
were put in on the form. It is controlled by a single button,
as in:

    <INPUT TYPE=submit NAME=mv_click VALUE="Search by Category">

When the user clicks the submit button, all three variables will take
on the values defined in the "Search by Category" scratch variable. You
can set the scratch variable on the same form as the button is on -- in
fact that is recommended for clarity.

The C<mv_click> variable will not be carried from form to form, it must be set
on the form being submitted.

The special variable C<mv_check> sets variables for the form actions
F<checkout, control, refresh, return, search,> and F<submit>. 
This function operates I<after> the values are set from the form,
including the ones set by C<mv_click>, and can be used to condition
input to search routines or orders.

The variable sets can contain and be generated by most MiniVend tags --
the profile is interpolated for MiniVend tags before being used. This
may not operate how you would expect -- for instance, if you set:

    [set check]
    [cgi name=mv_todo set=bar hide=1]
    mv_todo=search
    [if cgi mv_todo eq 'search']
    do something
    [/if]
    [/set]

The if condition is guaranteed to be false, because the tag interpretation
takes place before the evaluation of the variable setting.

Any setting of variables already containing a value will overwrite
the variable, so to build sets of fields (as in mv_search_field
and mv_return_fields) you must use comma separation if that is
supported for the field.

It is very convenient to use C<mv_click> as a trigger for embedded
Perl:

    <FORM ...
    <INPUT TYPE=hidden NAME=mv_check VALUE="Invalid Input">
    ...
    </FORM>

    [set Invalid Input]
    [perl]
    my $type        = $CGI->{mv_searchtype};
    my $spell_check = $CGI->{mv_spelling_errors};
    my $out = '';
    if($spell_check and $type eq 'text') {
        $CGI->{mv_todo}     = 'return';
        $CGI->{mv_nextpage} = 'special/cannot_spell_check';
    }
    return;
    [/perl]
    [/set]

=head2 Checks and Selections

You can provide a "memory" for drop-down menus, radio buttons, and
checkboxes with the C<[checked]> and C<[selected]> tags.

=over 4

=item [checked var_name value]

named attributes: [checked name="var_name" value="value" multiple=0|1 default=0|1]

This will output CHECKED if the variable C<var_name> is equal to
C<value>. Not case sensitive. 

If the C<multiple> attribute is defined and set to a non-zero
value (1 is implicit) then if the value matches on a word/non-word
boundary it will be CHECKED. If the C<default> attribute is set to a
non-zero value, then the box will be checked if the variable C<var_name>
is empty or zero.

=item [selected var_name value MULTIPLE*]

named attributes: C<[selected name=var_name value="value" multiple=1]>

This will output SELECTED if the variable C<var_name> is equal to
C<value>. If the optional MULTIPLE argument is present, it will
look for any of a variety of values. Not case sensitive.

Here is a drop-down menu that remembers an item-modifier
color selection:

    <SELECT NAME="color">
    <OPTION [selected name=color value=blue]> Blue
    <OPTION [selected name=color value=green]> Green
    <OPTION [selected name=color value=red]> Red
    </SELECT>

For databases or large lists of items, sometimes it is easier to
use C<[loop list="foo bar"]> and its C<option> parameter. The above
can be achieved with:

    <SELECT NAME=color>
    [loop list="Blue Green Red" option=color]
    <OPTION> [loop-code]
    [/loop]
    </SELECT>

=back

=head2 Integrated Image Maps

Imagemaps can also be defined on forms, with the special form variable
C<mv_todo.map>. A series of map actions can be defined --
the action specified in the I<default> entry will be applied if none
of the other coordinates match. The image is specified with a standard
HTML 2.0 form field of type I<IMAGE>. Here is an example:

 <INPUT TYPE=hidden NAME="mv_todo.map" VALUE="rect submit 0,0 100,20">
 <INPUT TYPE=hidden NAME="mv_todo.map" VALUE="rect cancel 290,2 342,18">
 <INPUT TYPE=hidden NAME="mv_todo.map" VALUE="default refresh">
 <INPUT TYPE=image  NAME="mv_todo" SRC="url_of_image">

All of the actions will be combined together into one image map with
NCSA-style functionality -- see the NCSA imagemap documentation for
details -- except that MiniVend form actions are defined instead
of URLs. The standard actions are:

 submit   Submit order
 refresh  Refresh order page (update quantities, etc.)
 cancel   Cancel order and wipe credit card numbers
 return   Go to previous page (or page defined in mv_nextpage variable)
 control  Control help, colors, etc.
 search   Search for an item in the catalog

=head2 Setting Form Security

You can cause a form to be submitted securely (to the base
URL in the SecureURL directive, that is) by specifying your form
input to be ACTION="[process secure=1]".

To submit a form to the regular non-secure server, just omit the
C<secure> modifier.

=head2 Stacking Variables on the Form

Many MiniVend variables can be "stacked", meaning they can have
multiple values for the same variable name. As an example -- to allow
the user to order multiple items with one click, you can set up
a form like this:

 <FORM METHOD=POST ACTION="[process-order]">
 <input type=checkbox name="mv_order_item" value="M3243"> Item M3243
 <input type=checkbox name="mv_order_item" value="M3244"> Item M3244
 <input type=checkbox name="mv_order_item" value="M3245"> Item M3245
 <input type=hidden name="mv_doit" value="refresh">
 <input type=submit name="mv_junk" value="Order Checked Items">
 </FORM>

The stackable C<mv_order_item> variable with be decoded with multiple
values, causing the order of any items that are checked.

To place a "delete" checkbox on your shopping basket display:

 <FORM METHOD=POST ACTION="[process-order]">
 [item-list]
   <input type=checkbox name="[quantity-name]" value="0"> Delete
   Part number: [item-code]
   Quantity: <input type=text name="[quantity-name]" value="[item-quantity]">
   Description: [item-description]
 [/item-list]
 <input type=hidden name="mv_doit" value="refresh">
 <input type=submit name="mv_junk" value="Order Checked Items">
 </FORM>

In this case, B<first> instance of the variable name set by C<[quantity-name]>
will be used as the order quantity, deleting the item from the form.

Of course, not all variables are stackable. Check the documentation for
which ones can be stacked -- or experiment on your own.

=head2 Extended Value Access and File Upload

MiniVend has a facility for greater control over the display of form
variables; it also can parse C<multipart/form-data> forms for file upload.

File upload is simple. You define a form like:

    <FORM ACTION="[process-target] METHOD=POST ENCTYPE="multipart/form-data">
    <INPUT TYPE=hidden NAME=mv_todo     VALUE=return>
    <INPUT TYPE=hidden NAME=mv_nextpage VALUE=test>
    <INPUT TYPE=file NAME=newfile>
    <INPUT TYPE=submit VALUE="Go!">
    </FORM>

The [value-extended ...] tag performs the fetch and storage
of the file.
If you put the below on the C<test.html> page (as you specified with
C<mv_nextpage> and use with the above form it will write the file
you have specified:

    <PRE>
    Uploaded file name: [value-extended name=newfile]
    Is newfile a file? [value-extended name=newfile yes=Yes no=No test=isfile]
 
    Write the file. [value-extended name=newfile outfile=junk.upload]
    Write again with
     indication: [value-extended name=newfile
                                outfile=junk.upload
                                yes="Written."]
                                no=FAILED]
 
    And the file contents:
    [value-extended name=newfile file_contents=1]
    </PRE>

The [value-extended] tag also allows access to the array values
of stacked variables. Use the following form:

    <FORM ACTION="[process-target] METHOD=POST ENCTYPE="multipart/form-data">
    <INPUT TYPE=hidden NAME=testvar VALUE="value0">
    <INPUT TYPE=hidden NAME=testvar VALUE="value1">
    <INPUT TYPE=hidden NAME=testvar VALUE="value2">
    <INPUT TYPE=submit VALUE="Go!">
    </FORM>

and page:

    testvar element 0: [value-extended name=testvar index=0]
    testvar element 1: [value-extended name=testvar index=1]
    testvar elements:
     joined with a space:   |[value-extended name=testvar]|
     joined with a newline: |[value-extended
                                joiner="\n"
                                name=testvar
                                index="*"]|
     first two only:    |[value-extended
                                name=testvar
                                index="0..1"]|
     first and last:    |[value-extended
                                name=testvar
                                index="0,2"]|

to observe this in action.

The syntax for C<[value-extended ...]> is:

 named: [value-extended 
            name=formfield
            outfile=filename*
            ascii=1*
            yes="Yes"*
            no="No"*
            joiner="char|string"*
            test="isfile|length|defined"*
            index="N|N..N|*"
            file_contents=1*
            elements=1*]

positional: [value-extended name]

Expands into the current value of the customer/form input field named
by field. If there are multiple elements of that variable, it will return
the value at C<index>; by default all joined together with a space.

If the variable is a file variable coming from a multipart/form-data
file upload, then the contents of that upload can be returned to the 
page or optionally written to the C<outfile>.

=over 4

=item name

The form variable NAME. If no other parameters are present, then the 
value of the variable will be returned. If there are multiple elements,
then by default they will all be returned joined by a space. If C<joiner>
is present, then they will be joined by its value.

In the special case of a file upload, the value returned is the name
of the file as passed for upload.

=item joiner

The character or string that will join the elements of the array. Will
accept string literals such as "\n" or "\r".

=item test

Three tests -- C<isfile> returns true if the variable is a file upload.
C<length> returns the length. C<defined> returns whether the value
has ever been set at all on a form.

=item index

The index of the element to return if not all are wanted. This is
useful especially for pre-setting multiple search variables. If set
to C<*>, will return all (joined by C<joiner>). If a range, such
as C<0 .. 2>, will return multiple elements.

=item file_contents

Returns the contents of a file upload if set to a non-blank, non-zero value.
If the variable is not a file, returns nothing.

=item outfile

Names a file to write the contents of a file upload to. It will not
accept an absolute file name; the name must be relative to the catalog
directory. If you wish to write images or other files that would go to
HTML space, you must use the HTTP server's C<Alias> facilities or 
make a symbolic link.

=item ascii

To do an auto-ASCII translation before writing the C<outfile>, set
the C<ascii> parameter to a non-blank, non-zero value. Default is no
translation.

=item yes

The value that will be returned if a test is true or a file is
written successfully. Defaults to C<1> for tests and the empty
string for uploads.

=item no

The value that will be returned if a test is false or a file write
fails. Defaults to the empty string.

=back


=head2 Updating MiniVend database tables with a form

Any MiniVend database can be updated with a form using the following method.
The MiniVend companion application C<MiniMate> uses this facility extensively.

NOTE: All operations are performed on the B<database>, not the ASCII
source file. You will have to perform a C<[tag export table_name][/tag]> operation
if you want the ASCII source file to reflect the results of the update.

You of course may insert or update records in any I<SQL> database with the
C<[sql set]> tag, but you may also do form-based updates or inserts.

In an update form, special MiniVend variables are used to select
the database parameters:

=over 4

=item mv_data_enable (scratch)

IMPORTANT: This must be set to a non-zero, non-blank value in the scratch
space to allow data set functions. Usually it is put in an mv_click that
precedes the data set function, i.e.:

    [set update_database]
    [if type=data term="userdb::trusted::[data session username]"]
        [set mv_data_enable]1[/set]
    [else]
        [set mv_data_enable]0[/set]
    [/else]
    [/if]
    [/set]
    <INPUT TYPE=hidden NAME=mv_click VALUE=update_database>

=item mv_data_table

The table to update.

=item mv_data_key

The field that is the primary key in the table. Must match the existing
database definition.

=item mv_data_function

UPDATE, INSERT or DELETE. The variable C<mv_data_verify> must be set
true on the form for a DELETE to occur.

=item mv_data_verify

Confirms a DELETE.

=item mv_data_fields

Fields from the form which should be inserted or updated. Must be existing
columns in the table in question.

=item mv_update_empty

Normally an variable that is blank will not replace the field. If you
set C<mv_update_empty> to true, a blank value will erase the field in
the database.

=item mv_data_filter_(field)

Instantiates a filter for C<(field)>, using any of the defined 
MiniVend filters. For example, if C<mv_data_filter_foo> is set
to C<digits>, only digits will be passed into the database field
during the set operation. A common value might be "entities", which
protects your HTML by translating C<<> into C<&lt;>, C<"> into C<&quot;>,
etc.

=back

The MiniVend action B<set> causes the update. Here is an pair of example forms.
One is used to set the key to access the record (careful with the name, this
one goes into the user session values). The second actually performs the
update. It uses the C<[loop]> tag with only one value to place default/existing
values in the form based on the input from the first form:

    <FORM METHOD=POST ACTION="[process-target]">
    <INPUT TYPE=HIDDEN name="mv_doit" value="return">
    <INPUT TYPE=HIDDEN name="mv_nextpage" value="update_proj">
    Sales Order Number <INPUT TYPE=TEXT SIZE=8
                            NAME="update_code"
                            VALUE="[value update_code]">
    <INPUT TYPE=SUBMIT name="mv_submit"  Value="Select">
    </FORM>



    [new]
    <FORM METHOD=POST ACTION="[process-target]">
    <INPUT TYPE=HIDDEN NAME="mv_data_table"    VALUE="ship_status">
    <INPUT TYPE=HIDDEN NAME="mv_data_key"      VALUE="code">
    <INPUT TYPE=HIDDEN NAME="mv_data_function" VALUE="update">
    <INPUT TYPE=HIDDEN NAME="mv_nextpage"      VALUE="updated">
    <INPUT TYPE=HIDDEN NAME="mv_data_fields"
                VALUE="code,custid,comments,status">
    <PRE>

    [loop arg="[value update_code]"]
    Sales Order <INPUT TYPE=TEXT NAME="code    SIZE=10 VALUE="[loop-code]">
   Customer No. <INPUT TYPE=TEXT NAME="custid" SIZE=30
                    VALUE="[loop-field custid]">
       Comments <INPUT TYPE=TEXT NAME="comments" 
                    SIZE=30 VALUE="[loop-field comments]">
         Status <INPUT TYPE=TEXT NAME="status"
                    SIZE=10 VALUE="[loop-field status]">
    [/loop]
    </PRE>

        <INPUT TYPE=hidden NAME="mv_todo" VALUE="set">
        <INPUT TYPE=submit VALUE="Update table">
    </FORM>

The variables in the form do B<not> update the user's session values,
so they can correspond to database field names without fear of corrupting
the user session.

=head1 THE SEARCH ENGINE

MiniVend implements a search engine which will search the product
database (or any other file) for items based on customer input. It uses
either forms or link-based searches that are called with the special page name
C<scan>. The search engine uses many special MiniVend tags and variables.

If the search is implemented in a link or a form, it will always
display formatted results on I<the results page>, a MiniVend page
that uses some combination of the C<[search-region]>, C<[search-list]>,
C<[more-list]>, C<[more]>, and other MiniVend tags to format and display
the results. The search results are usually a series of product codes/SKUs
or other database keys, which are then iterated over similar to the 
C<[item-list]>.

Examples of search forms and result pages are included in the supplied
demos.

Two search engine interfaces are provided, and five types of searching
are available. The default is a text-based search of the first
products database source file (i.e. products.txt). A binary search of
a dictionary-ordered file can be specified. An optional Glimpse search
is enabled by placing the command specification for Glimpse in the
C<catalog.cfg> directive I<Glimpse>.  There is a range-based search,
used in combination with one of the above.  And finally, there is a
fully-coordinated search with grouping.

The default, a text based search, sequentially scans the lines in
the target file. By default it returns the first field (delineated by
the delimiter for that database, for every line matching the search
specification. This corresponds to the product code, which is then
used to key specific accesses to the database.

The text-based search is capable of sophisticated field-specific
searches with fully-independent case-sensitivity, substring, and negated
matching. 

=head2 The Search Form

A number of variables can be set on search forms to determine which search
will be used, what fields in the database it will search, and what search
behavior will be.

Here is a simple search form:

  <FORM ACTION="[area search]" METHOD=POST>
  <INPUT TYPE="text" SIZE="30" NAME="mv_searchspec">
  <INPUT TYPE="submit" VALUE="Search">
  </FORM>

When the "Search" submit button is pressed (or <ENTER> is pressed)
MiniVend will search the C<products.txt> file for the string entered
into the text field C<mv_searchspec>, and return the product
code pertaining to that line.

The same search for a fixed string, say "shirt", could be performed with
the use of a hot link, using the special I<scan> URL:

  [page search="se=shirt"]See our shirt collection![/page]

The default is to search every field on the line.
If you only wished to match on the string shirt in the product
database field "description", you could modify the search:

  <INPUT TYPE="hidden" NAME="mv_search_field" VALUE="description">

In the hot-linked URL search:

  [page search="
                se=shirt
                sf=category
            "]See our shirt collection![/page]

If you want to let the user decide on the search parameters, you can
use checkboxes or radiobox fields to set the fields:

    Search by author
       <INPUT TYPE="checkbox" NAME="mv_search_field" VALUE="author">
    Search by title
        <INPUT TYPE="checkbox" NAME="mv_search_field" VALUE="title">

Fields can be stacked -- if more than one is checked, all checked fields
will be searched.

=head2 Glimpse

B<To use the Glimpse search>, you must build the Glimpse index based on
files in your I<ProductDir>, or wherever the files to be searched will
be located. If you installed MiniVend in the default
C</usr/local/lib/minivend>, the command line to build the index for the
products file would be:

    chdir products
    glimpseindex -b -H . products.txt

There are several ways to improve search speed for large catalogs.

One method that works well for large C<products.txt> files is to split
the C<products.txt> file into small index files (in the example, 100
lines) with the split(1) UNIX/POSIX command, then index it with glimpse:

    split -100 products.txt index.txt.
    glimpseindex -H /usr/local/lib/minivend/products index.txt.*

This will dramatically increase search speeds for large catalogs, at
least if the search term is relatively unique. If it is a common string,
as you might have in a category search, you will be better off to use
the text-based search.

If you are intending to search for numbers, add the C<-n> option to
the Glimpse command line.

(A large catalog is one of more than several thousand items -- smaller
ones have acceptable speed in any of the search modes.)

If the Glimpse executable is not found at MiniVend startup, the Glimpse
search will be disabled and the regular text-based search used instead.

There are several things you have to watch for while using B<glimpse>,
and a liberal dose of the Glimpse documentation is suggested. In particular,
the spelling error capability will not work in combination with the
field-specific search -- Glimpse selects the line, but MiniVend's
text-based search routines disqualify it when checking to see if the
search string is within one of the specified fields.

To use field-specific searching on Glimpse, you need to tell it what
the field names are. If your search is on your products database (file),
nothing is needed, for the default is to use the field names from the
products database. If it is some other field layout, specify the file
to get the field names from with C<mv_field_file> (ff).

=head2 Fast Binary Search

B<Fast binary searching> is useful for scanning large databases for
strings that match the beginning of a line. They use the standard
Perl module Search::Dict, and are enabled through use of the C<mv_dict_look>,
C<mv_dict_end>, C<mv_dict_limit>, C<mv_dict_fold>, and C<mv_dict_order>
variables.

The field to search is the first field in the file, then the
product code should be in the second field, delimited by TAB.
You will also have to set mv_return_fields=1 to return the product code
in the search.

The search must be done on a B<dictionary-ordered> pre-built index, 
which can be produced with the database INDEX modifier. See 
I<Dictionary indexing with INDEX>.

If you use the C<mv_dict_look> parameter by itself, and the proper
index file is present, MiniVend will set the options:

    mv_return_fields=1
    mv_dict_limit=-1

This will make the search behave much like the simple search described
above, except it will be B<much> faster on large files and will match
only from the B<beginning> of the field.

Here is an example. You have built a C<title> index by including in
catalog.cfg:

    Database   products   INDEX    title

NOTE: The ASCII source file must be "touched" to rebuild the index and the
databse.

Now you can specify in a form:

    <FORM ACTION="[process href=search]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_dict_limit VALUE=title>
    <INPUT NAME=mv_dict_look>
    </FORM>

or in a URL:

    [page search="dl=Van Gogh/di=title"]

This search is case-sensitive. To do the same thing case-insensitively:

    Database   products   INDEX    title:f
    
    <FORM ACTION="[process href=search]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_dict_limit VALUE=title>
    <INPUT TYPE=hidden NAME=mv_dict_fold  VALUE=1>
    <INPUT NAME=mv_dict_look>
    </FORM>
  
    [page search="dl=Van Gogh/di=title/df=1"]

=head2 Coordinated and joined searching

MiniVend will do a complete range of tests on individual columns in the
database. To use this function, set C<mv_coordinate> to B<yes> (co=yes in
the one-click syntax).

In order to use coordinated searching, the number of I<search fields>
B<must equal> the number of I<search strings>. This makes sense if you
think about it a bit.

If you want to make sure that is the case, use the C<mv_search_map>
variable. It allows you to map variables to others in the search
specification. For example:

    <INPUT TYPE=hidden NAME=mv_search_map VALUE="
        mv_searchspec=search1
        mv_searchspec=search2
        mv_searchspec=search3
        ">
    <INPUT TYPE=hidden NAME=mv_search_field VALUE=title>
    <INPUT TYPE=hidden NAME=mv_search_field VALUE=artist>
    <INPUT TYPE=hidden NAME=mv_search_field VALUE=category>
    Artist: <INPUT NAME=search1 VALUE="">
    Title:  <INPUT NAME=search2 VALUE="">
    Genre:  <INPUT NAME=search3 VALUE="">

Even if the user leaves one blank, the search will work.

Leading/trailing whitespace is stripped from all lines in the mv_search_map
variable, so you can position it as shown for convenience.

Coordinated searches may be joined with the output of another table
if you set one of the C<mv_search_field> values to a C<table:column>
pair. Note that this will slow down large searches considerably unless
you have another search specification, as the database must be accessed
for every search line! If you have a search field that qualifies for a
regular expression search function, or are doing a binary search with
C<mv_dict_look>, or are not doing an C<OR> search, the penalty should not
be too great as only matching lines will cause an access to the database.

Individual field operations can then be specified with the C<mv_column_op>
(or B<op>) parameter. The operations include:

    operation            string     numeric   equivalent
    ---------           
    equal to               eq         ==           =
    not equal              ne         !=           <>
    greater than           gt         > 
    less than              lt         < 
    less than/equal to     le         <= 
    greater than/equal to  ge         >= 
    regular expression     rm                       =~ , LIKE
    regular expression NOT rn                       !~
    exact match            em


An example:

    [page scan
            co=yes
            sf=title
            se=Sunflowers
            op=em
            sf=artist
            se=Van Gogh
            op=rm          ] Sunflowers, Van Gogh [/page]

    [page search="
            co=yes
  
            sf=title
            se=Sunflowers
            nu=0
            op=!~
  
            sf=artist
            se=Van Gogh
            op=rm    
            nu=0

            sf=inventory:qty
            se=1
            op=>=
            nu=1 
        "] Any in stock except Sunflowers, Van Gogh [/page]

Note that in the second example you need to specify nu=0 even though
that is the default. This is to set the proper correspondence. To avoid
having to do this, use MiniVend's option array feature:


    [page search.0="
                    sf=title
                    se=Sunflowers
                    op=!~
                "
          search.1="
                    sf=artist
                    se=Van Gogh
                "
          search.2="
                    sf=inventory:qty
                    se=1
                    op=>=
                    nu=1 
                "
        ] Any in stock except Sunflowers, Van Gogh [/page]

The C<co=yes> is assumed when specifying a multiple search.

The second search will check the stock status of the painting provided
there is an C<inventory> table as in some of the MiniVend demo catalogs.
If the C<qty> field is greater than or equal to 1, then the product will
be picked. If out of stock, it will not be found.

It always helps to have an C<rm> type included in the search. This is
used to pre-screen records so that database accesses only need be made
for already-matching entries. If accesses must be made for every record
large searches can get quite slow.

=head2 Specifying a text-based search with SQL-like syntax

If you have installed Jochen Wiedmann's C<SQL::Statement> module, you can
specify an SQL syntax for the text-based search. (This is B<not> the same
as the the SQL search, treated below separately. It would work on an SQL
table but only on the ASCII text source file, not on the actual database.)

This syntax allows this rather nice form setup:

    Artist: <INPUT NAME="artist">
    Title:  <INPUT NAME="title">
    <INPUT TYPE=hidden NAME="mv_sql_query"
            VALUE="
                SELECT code FROM products
                WHERE artist LIKE artist
                AND    title LIKE title">                             

If the right hand side of an expression looks like a column, i.e. is not
quoted, then the appropriate form variable is substituted. (If used in
a one-click, the corresponding I<scratch> variable is used instead.)
The assumption is reversed for the left-hand side -- if it is a quoted
string then the column name is read from the passed values -- otherwise
the column name is literal.                     

    Search for: <INPUT NAME="searchstring"><BR>                
    Search in   <INPUT TYPE="radio" NAME="column" VALUE="title"> title
        <INPUT TYPE="radio" NAME="column" VALUE="artist"> artist
        <INPUT TYPE=hidden NAME="mv_sql_query"                            
          VALUE="SELECT code FROM products WHERE 'column' LIKE searchstring">

Once again, this does not do a search on an SQL database, but formats
a corresponding text-based search. Parentheses will have no effect, and 
an OR condition will cause all conditions to be OR. The searches above
would be similar to:

    [page search="
                co=yes
                sf=artist
                op=rm
                se=[value artist]
                sf=title
                op=rm
                se=[value title]
            "  ]
        Search for [value artist], [value title]
    [/page]
   
    [page search="
                co=yes
                sf=[value column]
                op=rm
                se=[value searchstring]
            "  ] 
    Search for [value searchstring]
           in  [value column]
    [/page]

=head2 Range Searching

Range searching allows you to qualify your search returns with a field
that must be within a certain numeric or alphanumeric range. To use it,
set the mv_range_look variable to the products database field, or a
column/field number for another file. Then set the corresponding
C<mv_range_min> and C<mv_range_max> variables with a selectable field.

    <INPUT TYPE="hidden" NAME="mv_range_look" VALUE="price">
        Search on Price
    Min <SELECT NAME="mv_range_min">
             <OPTION value=0 SELECTED> Free
             <OPTION value=1000000> $1,000,000
             <OPTION value=10000000> $10,000,000
             <OPTION value=20000000> $20,000,000
             <OPTION value=40000000> $40,000,000
        </SELECT><BR>
    Max <SELECT NAME="mv_range_max">
            <OPTION value=0 SELECTED> no object
            <OPTION value=1000000> $1,000,000
            <OPTION value=10000000> $10,000,000
            <OPTION value=20000000> $20,000,000
            <OPTION value=40000000> $40,000,000
        </SELECT>

The value of 0 for mv_range_max is equivalent to infinity if doing a
numeric search. (This makes it impossible to search for a ceiling of 0
with a negative mv_range_min, just in case you were planning on trying
that.)

The fields are stackable, so you can set more than one range to
check. The order is significant, in the sense that the array
of field names and minimum/maximum values must be kept in order
to achieve correspondence.

The optional C<mv_range_alpha> specification allows alphanumeric range
matching for the corresponding field -- if it is set, and you have
stacked the fields, they must all be set. The C<mv_case> field does
apply if it is set -- otherwise the comparison is without regard to
case.

If you wish to do ONLY a range search, you must select all lines
with C<mv_return_all>=yes in order to make the search operate. Range-only
searches will be quite slow for large databases, since every line
must be scanned. It should be quite usable for catalogs of less than
10,000 items in size, given a fast machine. Using it in combination
with another search technique (in the same query) will yield faster search
returns.

=head2 One-click searches

MiniVend allows you to pass a search in a URL, as shown before. Just
specify the search with the special page parameter search or
special page C<scan>. Here is an example:

     [page search="
                se=Impressionists
                sf=category
            "]
        Impressionist Paintings
     [/page]

This is the same:

     [page scan se=Impressionists/sf=category]
        Impressionist Paintings
     [/page]

Here is the same thing from a home page (assuming /cgi-bin/vlink is
the CGI path for MiniVend's vlink):

     <A HREF="/cgi-bin/vlink/scan/se=Impressionists/sf=category">
        Impressionist Paintings
     </A>

The two-letter abbreviations are mapped with these letters:

  ac  mv_all_chars
  bd  mv_base_directory
  bs  mv_begin_string
  co  mv_coordinate
  cs  mv_case
  cv  mv_verbatim_columns
  de  mv_dict_end
  df  mv_dict_fold
  di  mv_dict_limit
  dl  mv_dict_look
  DL  mv_raw_dict_look
  do  mv_dict_order
  dr  mv_record_delim
  em  mv_exact_match
  er  mv_spelling_errors
  ff  mv_field_file
  fi  mv_search_file
  fm  mv_first_match
  fn  mv_field_names
  hs  mv_head_skip
  ix  mv_index_delim
  lb  mv_search_label
  lo  mv_list_only
  lr  mv_search_line_return
  md  mv_more_decade
  ml  mv_matchlimit
  mm  mv_max_matches
  MM  mv_more_matches
  mp  mv_profile
  ms  mv_min_string
  ne  mv_negate
  ng  mv_negate
  np  mv_nextpage
  nu  mv_numeric
  op  mv_column_op
  os  mv_orsearch
  ra  mv_return_all
  rd  mv_return_delim
  rf  mv_return_fields
  rg  mv_range_alpha
  rl  mv_range_look
  rm  mv_range_min
  rn  mv_return_file_name
  rr  mv_return_reference
  rs  mv_return_spec
  rx  mv_range_max
  SE  mv_raw_searchspec
  se  mv_searchspec
  sf  mv_search_field
  sg  mv_search_group
  si  mv_search_immediate
  sp  mv_search_page
  sq  mv_sql_query
  sr  mv_search_relate
  st  mv_searchtype
  su  mv_substring_match
  tc  mv_sort_command
  td  mv_table_cell
  tf  mv_sort_field
  th  mv_table_header
  to  mv_sort_option
  tr  mv_table_row
  un  mv_unique
  va  mv_value

They can be treated just the same as form variables on the page, except that
they can't contain a newline. If you use the multi-line method of specification,
the characters will automatically be escaped for a URL.

B<IMPORTANT NOTE:> An incompatibility in earlier MiniVend catalogs
is specifying C<[page scan/se=searchstring]>. This is
interpreted by the parser as C<[page scan/se="searchstring"]> and will
cause a bad URL. Change this to C<[page scan se=searchstring]>, or
perhaps better yet:

    [page search="
                    se=searchstring
            "]
          
cause a bad URL. Change this to C<[page scan se=searchstring]>.

You may specify a one-click search in three different ways.

=over 4

=item Original

If you wish to do an OR search on the fields category and artist
for the strings "Surreal" and "Gogh", while matching substrings,
you would do:

 [page scan se=Surreal/se=Gogh/os=yes/su=yes/sf=artist/sf=category]
    Van Gogh -- compare to surrealists
 [/page]

In this method of specification, to replace a / (slash) in a file name
(for the sp, bd, or fi parameter) you must use the shorthand of ::,
i.e. sp=results::standard. (This may not work for some browsers, so you
should probably either put the page in the main pages directory or define
the page in a search profile.)

=item Multi-line

You can specify parameters one to a line, as well. 

    [page scan
        se="Van Gogh"
        sp=lists/surreal
        os=yes
        su=yes
        sf=artist
        sf=category
    ] Van Gogh -- compare to surrealists [/page]

Any "unsafe" characters will be escaped. If you need to search for
trailing spaces (unlikely) you must quote.

=item Ampersand

You can substitute & for / in the specification and be able to use / and
quotes and spaces in the specification.

 [page href=scan se="Van Gogh"&sp=lists/surreal&os=yes&su=yes&sf=artist&sf=category]
    Van Gogh -- compare to surrealists
 [/page]

Any "unsafe" characters will be escaped. 

=back

=head2 Setting display options with mv_value

You can specify a value that will be set in the link with the C<mv_value>
parameter. It takes an argument of C<var=value>, just as you would set a
normal variable in a MiniVend profile. Actually C<mv_value> is a misnomer,
for you would almost never use it in a form, where you can easily set
variable values. You will always specify it in a one-click search with
C<va=var=value>. Example:

    [page href=scan
          arg="se=Renaissance
               se=Impressionists
               va=category_name=Renaissance and Impressionist Paintings
               os=yes"]Renaissance and Impressionist Paintings[/page]

Now you can display the appropriate category on the search results
page with C<[value category_name]>.

=head2 In-page searches

You may specify a search inside a page with the
C<[search-region parameters*]> tag. The parameters are the same as the the
one-click search, and the output is always a newline-separated list of
the return objects -- by default a series of item codes.

The C<[loop ...]> tag directly accepts a search parameter. To search for
all products in the categories "Americana" and "Contemporary" you can do:

    [loop search="
        se=Americana
        se=Contemporary
        os=yes
        sf=category9
        "]
    Artist: [loop-field artist]<BR>
    Title: [loop-field title]<P>
    [/loop]

The advantage of the in-page search is that you can embed searches within
searches, and you can have straight unchanging links from static HTML pages.

To place an in page search with the full range of display in a normal
results page, use the C<[search-region]> tag the same as above, except
that you can place C<[search-list]>, C<[more-list]>, and C<[more]>
tags within it and use them to display and format the results --
including paging.

For example:

    [search-region  more=1
                    search="
                         se=Americana
                         sf=category
                         ml=2
                    "]
    [more-list][more][/more-list]
    [search-list]
    <A MV="page [item-code]" HREF="flypage.html">
        [item-field title]<A>, by [item-field artist]
    [/search-list]
    [no-match]
        Sorry, no matches for [value mv_searchspec].
    [/no-match]
    [/search-region]

If you want to use the same page for search paging, make sure you set
the C<sp=page> parameter.

=head2 Search Profiles

You can predefine an unlimited number of search profiles that reside
in a file or files. To use this, make up a series of lines like:

 mv_search_field=artist
 mv_search_field=category
 mv_orsearch=yes

These correspond to the MiniVend search variables that can
be set on a form. You can set it right on the page that contains
the search.

    [set artist_profile]
    mv_search_field=artist
    mv_search_field=category
    mv_orsearch=yes
    [/set]

This is the same:

    [set artist_profile]
    sf=artist
    sf=category
    os=yes
    [/set]

Then in the search form, set a variable with the name of the profile:

    <INPUT TYPE=hidden NAME=mv_profile VALUE=artist_profile>

In a one-click search, you use the C<mp> modifier:

    [page scan se=Leonardo/mp=artist_profile]A left-handed artist[/page]

You can also place them in a file. Define the file name in the
I<SearchProfile> directive. (You must reconfig the catalog for MiniVend
to read it.)  The profile is named by placing a name following a __NAME__
pragma:

  __NAME__ title_search

The __NAME__ must begin the line, and be followed by whitespace
and then the name.

The special variable C<mv_last> stops interpretation of search
variables. The following variables are always interpreted:

    mv_dict_look
    mv_searchspec
    mv_range_look
    mv_range_min
    mv_range_max

Other than that, if you set mv_last in a search profile, and there
are other variables on the search form, they will not be interpreted.

If you want to place multiple search profiles in the same file,
separate them with __END__, which must be on a line by itself.

=head2 Search Reference

The supplied C<simple/srchform.html> and C<simple/results.html> pages
show example search forms. You can modify them to present the search
in any way you like -- just be careful to use the proper variable names
for passing to MiniVend. It is also necessary that you copy the hidden
variables as-is -- they are required to interpret the request as a
search.

B<NOTE:> The following definitions frequently refer to F<field name>
and I<column> and I<column number> -- all are the references to the
columns of a searched text file as separated by delimiter characters.

The field names can be specified in several ways.

=over 4

=item ProductFiles

If the file to be searched is left empty in the search form or definition (it
is set with C<mv_search_file (fi)>), then the text files associated with the
products databases will be searched, and field names are already available as
named in the first line of the file(s). (This is defined to be C<products.txt>
in the MiniVend demonstrations.)

Careful if you are using SQL! If you change your database and don't 
export it with C<[tag export products][/tag]>, you will not find what
you want.

=item other database files

If the file or files to be searched are ASCII delimited files, and
have field names specified on the first line of the file, MiniVend
will read the first line (of the first file) and determine the field
names.

=item other files

If the file or files to be searched are ASCII delimited files, but
B<don't> have field names specified on the first line of the file,
you can set the variable C<mv_field_names> to a comma-separated list
of field names as they will be referenced.

=back

Fields can also always be specified by an integer column number, with 0
as the first column.

=over 4

=item mv_all_chars

Scan abbreviation: ac=[1|0]

Set this if you anticipate searching for lots of punctuation characters
that might be special characters for Perl -- the characters ()[]\$^ are
included.

=item mv_base_directory

Scan abbreviation: bd=/directory/name

In the text search, set to the directory from which to base file
searches. File names without leading / characters will be based from
there. In the Glimpse search, passed to Glimpse with the C<-H> option,
and Glimpse will look for its indices there. Default is ProductDir.

If you use an absolute path directory, for security you must enable it
in the users session with:

    [set /directory/name]1[/set]

This prevents users from setting an arbitrary value and viewing arbitrary
files.

=item mv_begin_string

If this is set, the string will only match if it is at the beginning
of a field. The handling is a bit different for the default AND search
compared to the OR search. With OR searches all words are searched
for from the beginning of the field, with AND searches all are.

This is a multiple parameter. If mv_coordinate is in force, then it
should be set as many times as necessary to match the field/searchstring
combination. If set only once, it applies to all fields. If set more than
once but not as many times as the fields, it will default to off.

=item mv_case

If this item is set to C<no>, the search will return items without regard
to upper or lower case. This is the default -- set to C<yes> if case
should be matched. Implement with a checkbox <INPUT TYPE=CHECKBOX> field.

If stacked to match the mv_search_field and mv_searchspec variables,
and C<mv_coordinate> is set, it will operate only for the corresponding
field.

=item mv_coordinate

If this item is set to I<yes>, and the number of search fields equals
the number of search specs, the search will return only items that
match field to spec. (The search specifications are set by stacked
C<mv_searchspec> and C<mv_search_field> variables.)

Case sensitivity, substring matching, and negation all work on a field-by
field basis according to the following:

=over 2

=item *

If only one instance of the option is set, then it will
affect all fields.

=item *

If the number of instances of the option is greater than or equal
to the number of search specs, all will be used independently. Trailing
instances will be ignored.

=item *

If more than one instance of the options are set, but fewer than the
number of search specifications, the default setting will be used for
the trailing unset options.

If a search specification is blank, it will be removed and all 
case-sensitivity/negation/substring options will be adjusted accordingly.

=back

=item mv_dict_end

If the string at the beginning of a line lexically exceeds this value,
matching will stop. Ignored without mv_dict_look.

=item mv_dict_fold

Make dictionary matching case-insensitive. Ignored without mv_dict_look.
NOTE: This is the reverse sense from mv_case.

=item mv_dict_limit

Automatically set the limiting string (mv_dict_end) to be one character
greater than the mv_dict_look variable, at the character position
specified. A value of 1, for instance, will set the limiting string
to "fprsythe" if the value of C<mv_dict_look> is "forsythe". A useful
value is -1, which will increment the last character (setting the 
mv_dict_end to "forsythf" in our example). This prevents having
to scan the whole file once a unique match is found.

The order of this and the C<mv_dict_end> variable is significant -- each
will overwrite the other.

If this is set to a non-numeric value, an automatic mode is entered which
looks for a dictionary-indexed file that corresponds to the file name plus
C<.field>, where C<field> is what you have set C<mv_dict_limit> to. The actual
value of mv_dict_limit is set to C<-1>. If the file does not exist, then
the original file is silently used (this might not be what you want!). Also,
the value of C<mv_return_fields> is set to C<1> to correspond to the location
of the key in the auto-indexed file.

To illustrate:

    <INPUT TYPE=hidden NAME=mv_dict_limit  VALUE=category>
    <INPUT TYPE=hidden NAME=mv_search_file VALUE="products.txt">

is equal to:

    <INPUT TYPE=hidden NAME=mv_dict_limit    VALUE="-1">
    <INPUT TYPE=hidden NAME=mv_search_file   VALUE="products.txt.category">
    <INPUT TYPE=hidden NAME=mv_return_fields VALUE="1">

The real utility would be in a form construct like

    Search for
    <SELECT NAME=mv_dict_limit>
    <OPTION> author
    <OPTION> title
    </SELECT> beginning with <INPUT NAME=mv_dictlook>

which would allow automatic binary search file selection.

Combined with the C<INDEX> attribute to the I<Database> directive,
this allows fast binary search qualification combined with regular
C<mv_searchspec> text searches.

=item mv_dict_look

The string at which to begin matching at in a dictionary-based search. If
not set, the mv_dict_end, mv_dict_fold, and mv_dict_case variables will
be ignored. May be set in a search profile based on other form variables.

=item mv_dict_order

Make dictionary matching follow dictionary order, where only word characters
and whitespace matter. Ignored without mv_dict_look.

=item mv_doit

This must be set to C<search> to make this a search page.

=item mv_exact_match

Normally MiniVend searches match words, as opposed to sentences.
This behavior can be overridden with mv_exact_match, which when
set will place quotes around any value in mv_searchspec or mv_dict_look.

=item mv_field_names

Deprecated in favor of in-list sorting.

Defines the field names for the file being searched. This guarantees
that they will be available, and prevents a disk access if using named
fields on a search file (that is not the product database ASCII source,
where field names are already known). This B<must> be exactly correct,
or it will result in anomalous search operation. Usually passed in a
hidden field or search profile as a comma-separated list.

NOTE: You should use this on the product database only if you
plan on both pre-sorting with C<mv_sort_field> and then post-sorting
C<with [sort]field:opt[/sort]>.

=item mv_first_match

Normally MiniVend will return the first page of a search. If you set
this variable it will start the search return at the match specified,
even if there is only one page. If set to a value greater than the
number of matches, it will act as if no matches were found.

=item mv_head_skip

Normally MiniVend searches all lines of an index/product file but the
first. Set this to the number of lines to skip at the beginning of the
index. Default is 1 for the text search, which skips the header line
in the product file. Default is 0 for a Glimpse search.

=item mv_index_delim

Sets the delimiter for counting fields in a search index. The default
is TAB.

=item mv_matchlimit

The page size for matches that are returned. If more matches than
B<mv_matchlimit> are found, the search paging mechanism will be
employed if the proper C<[more-list]> is present. Can be
implemented as a scrolling list (INPUT TYPE=SELECT) or radiobox 
(INPUT TYPE=RADIO) field.

=item mv_max_matches

The maximum number of records that will be returned in a search.
Default is 2000. B<This only applies to Glimpse>.
Use mv_matchlimit to set the search page size.

=item mv_min_string

Sets the minimum size of a search string for a search operation.
Default is 4 for the Glimpse search, and 1 for the text search.

=item mv_negate

Specifies that records NOT matching the search criteria will be returned.
Default is no. It is not operative for the Glimpse search.

If stacked to match the mv_search_field and mv_searchspec variables,
and mv_coordinate is set, it will operate only for the corresponding
field.

=item mv_orsearch

If this item is set to I<yes>, the search will return items matching any
of the words in I<searchspec>. The default is I<no>.

=item mv_profile

Selects one of the pre-defined search specifications set by the
I<SearchProfile> directive. If the special variable within that file,
C<mv_last>, is defined, it will prevent the scanning of the form input
for further search modifications. The values of C<mv_searchspec> and
C<mv_dict_look> are always scanned, so you can specify this to do the
equivalent of setting multiple checkboxes or radioboxes with one click,
while still reading the search input text.

=item mv_range_alpha

Sets the type of match, numeric or alphanumeric,  for the range search
in its corresponding range field. The search will return true (assuming
it is greater than the mv_range_min specification) if the field searched
is less than or equal to mv_range_max, in an alphanumeric sense.

=item mv_range_look

This sets a field to scan for a range of numbers. It must be
accompanied with corresponding mv_range_min and mv_range_max variables.
It can be specified with either a field name or a column number.

=item mv_range_max

Sets the high bound for the range search in its corresponding 
range field. The search will return true (assuming it is greater than
the mv_range_min specification) if the field searched is less than or
equal to mv_range_max. To set the bound at infinity, or whatever your
integer limit is, set mv_range_min to 0.

=item mv_range_min

Sets the low bound for the range search in its corresponding 
range field. The search will return true (assuming it is less than
the mv_range_max specification) if the field searched is less than or
equal to mv_range_min.

=item mv_record_delim

Sets the delimiter for counting records in a search index. The default
is newline, which works for the products and most line-based index files.

=item mv_return_fields

The fields that should be returned by the match, specified either by
field name or by column number. You should almost always specify
0 as the first field to be returned if searching the products database,
since that is the key for accessing database fields.

=item mv_return_spec

Returns the string specified as the search (i.e. the value of C<mv_searchspec>)
as the one and only match. Typically used in a SKU/part number search.

=item mv_search_field

The field(s) to be searched, specified either by column name
or by column number.

If the number of instances matches the number of fields specified in the
C<mv_searchspec> variable, and C<mv_coordinate> is set to true, each search
field (in order specified on the form) will be matched with each search
spec (again in that order).

=item mv_search_file

In the text search, set this variable to the file(s) to be scanned for
a match. The default, if not set, is to scan the default ProductFiles
(i.e. products.txt).  If set multiple times in a form (for a text search),
will cause a search all the files. One file name per instance.

In the Glimpse search, follows the Glimpse wildcard-based file name
matching scheme. Use with caution and a liberal dose of the Glimpse
man page.

=item mv_search_match_count

Set by the search to indicate the total number of matches found.

=item mv_search_over_msg

The message that should be displayed if there is an overflow
condition (C<mv_matchlimit> is exceeded). Overrides the SearchOverMsg
directive -- it is cleared by MiniVend if there is no overflow.
Somewhat deprecated by match paging.

=item mv_search_page

The MiniVend-style name of the page that should display the search
results. Overrides the FrameSearchPage directive, and the default
value of C<search>.

=item mv_searchspec

The actual search string that is typed in by the customer. It is 
a text INPUT TYPE=TEXT field, or can be put in a select (drop-down)
list to enable category searches. If multiple instances are found,
they will be concatenated just as if multiple words had been 
placed in a text field.

The user can place quotes around words to specify that they match
as a string. To enable this by default, use the C<mv_exact_match>
variable.

If C<mv_dict_look> has a value, and C<mv_searchspec> does B<not>, then
mv_searchspec will be set to the value of mv_dict_look.

If the number of instances matches the number of fields specified in the
C<mv_search_field> variable, and C<mv_coordinate> is set to true, each search
field (in order specified on the form) will be matched with each search
spec (again in that order).

=item mv_searchtype

If set to I<glimpse>, selects the Glimpse search (if Glimpse is defined).

If set to I<sql>, formulates an SQL select statement to return the
search list. 

If set to I<db>, iterates over every row of the database (not the
associated text source file).

If set to I<text>, selects the text-based search.

Defaults to I<text> if I<Glimpse> is not defined, to Glimpse if it
is. This can allow use of both search types if that is desirable --
for instance, searching for very common strings is better done by the
text-based search. An example might be searching for categories of items
instead of individual items.

=item mv_sort_field

The file field(s) the search is to be sorted on, specified in one of two
ways. If the file(s) to be searched have a header line (the first line)
that contains delimiter-separated field names, it can be specified
by field name. If can also be specified by column number (the code or key
is specified with a value of 0, for both types). These can be stacked,
if coming from a form, or placed in a single specification separated
by commas.

NOTE FOR ADVANCED USERS: If specifying a sort for the product database,
C<mv_field_names> must be specified if you will be doing a fieldname-addressed
post-sort.

=item mv_sort_option

The way that each field should be sorted. The flags are C<r>, C<n>, 
and C<f> -- for reverse, numeric, and case-insensitive respectively.
These can be stacked, if coming from a form, or placed in a single
specification separated by commas. The stacked options will be 
applied to the sort fields as they are defined, presuming those
are stacked.

=item mv_spelling_errors

The number of spelling errors that will be tolerated. Ignored unless
using Glimpse. If you have a large catalog, you might wish to limit this
to two.

=item mv_substring_match

If C<mv_substring_match> is  set to I<yes>, matches on substrings as well
as whole words. You would typically want to set this for dictionary-based
searches.

If stacked to match the C<mv_search_field> and C<mv_searchspec> variables,
and C<mv_coordinate> is set, it will operate only for the corresponding
field.

=item mv_unique

If set to a true value, causes the sort to return only unique results.
This operates on whatever the search return is, as defined by
C<mv_return_fields>.

=item mv_value

This is normally only used in the one-click search (va=var=value).
It allows setting of a session variable based on the clicked link,
which makes for easy definition of headers and other display 
choices. (If you had trouble using C<mv_searchspec> for this before,
this is what you need.)

=back


=head2 The Results Page

Once a search has been done, there needs to be a way of presenting
the output. By default, the I<SpecialPage> search is used -- it is
set to C<results> in the distribution demo -- but
any number of search pages can be specified by passing the value in
the search form, specified in the variable C<mv_search_page>.

On the search page, some special MiniVend tags are used to format
the otherwise standard HTML. Each of the iterative tags is
applied to every code returned from the search -- this is normally
the product code, but could be a key to any of the arbitrary
databases. The value placed by the C<[item-code]> tag is set to
the first field returned from the search.

The basic structure looks like this:

[search-region]
[search-list]
	your iterating code, once for each match
[/search-list]
[no-match] 
	Text / tags to be output if no matches found (optional but recommended)
[/no-match]
[more-list]
	More / paging area (optional)
[/more-list]
[/search-region]

TIP FOR MV3 PORTS: A C<[search-list][/search-list]> must always be surrounded by a
C<[search-region][/search-region]> pair. This is a change from Minivend 3.

=over 4

=item [search-list]

Starts the representation of a search list. MiniVend tags can be
embedded in the search list, yielding a table or formatted
list of items with part number, description, price, and  hyperlinks to
order or go to its catalog page. 

The example tags shown have an C<item-> prefix, which is the
default. You can set any prefix you want with the C<prefix> parameter
to C<[search-region]>:

	[search-region prefix=my]
	[search-list]
		SKU:   [my-code]
		Title: [my-data products title]
	[/search-list]
	[/search-region]
		
The standard set of Minivend iterative MML tags are available. They are
interpolated in this order:

		[item-alternate N] true [else] false [/else] [/item-alternate]
		[if-item-param named_field] true [else] false [/else] [/if-item-param]
		[item-param named_field]
		[if-item-pos N] true [else] false [/else] [/if-item-pos]
		[item-pos N]
		[if-item-field products_field] true [else] false [/else] [/if-item-field]
		[item-field products_column]
		[item-increment]
		[item-accessories]
		[item-code]
		[item-description]
		[if-item-data table column] true [else] false [/else] [/if-item-data]
		[item-data table column]
		[item-price N* noformat=1*]
		[item-calc] [/item-calc]
		[item-change marker]
			[condition]variable text[/condition]
			true
			[else] false [/else]
		[/item-change marker]
		[item-last] condition [/item-last]
		[item-next] condition [/item-next]

NOTE: those that reference the shopping cart do not apply, i.e. [item-quantity],
[item-modifier ...] and friends.

=item [/search-list]

Ends the search list. 

=item [no-match]

Starts the region of the search results page that should be returned
if there is no match (and no error) for the search. If this is not
on the page, the special page I<nomatch> will be displayed instead.

=item [/no-match]

Ends the no match region.

=item [sort database:field:option* database:field:option*]

Sorts the search list return based on database fields. If no
options are supplied, sorts according to the return code. See
I<SORTING>.

This is slow, and it is far better to pre-sort your return in the 
search specification.

=item [item-change marker]

Active only within C<[search-list][/search-list]>.

Along with the companion C<[/item-change marker]>, surrounds a region which
should only be output when a field (or other repeating value) changes its
value. This allows indented lists similar to database reports to be easily
formatted.  The repeating value must be a tag interpolated in the search
process, such as C<[item-field field]> or C<[item-data database field]>.

Of course, this will only work as you expect when the search results
are properly sorted.

The C<marker> field is mandatory, and is also arbitrary, meaning that
you can select any marker you wish as long as it matches the marker
associated with C<[/item-change marker]>. The value to be tested is contained
within a C<[condition]value[/condition]> tag pair. The C<[item-change marker]> tag
also processes an C<[else] [/else]> pair for output when the value does
not change. The tags may be nested as long as the markers are different.

Here is a simple example for a search list that has a field C<category> and
C<subcategory> associated with each item:

 <TABLE>
 <TR><TH>Category</TH><TH>Subcategory</TH><TH>Product</TH></TR>
 [search-list]
 <TR>
    <TD>
         [item-change cat]
 
         [condition][item-field category][/condition]
 
                 [item-field category]
         [else]
                 &nbsp;
         [/else]
         [/item-change cat]
    </TD>
    <TD>
         [item-change subcat]
 
         [condition][item-field subcategory][/condition]
 
                 [item-field subcategory]
         [else]
                 &nbsp;
         [/else]
         [/item-change subcat]
    </TD>
    <TD> [item-field name] </TD>
 [/search-list]
 </TABLE>

The above should put out a table that only shows the category and
subcategory once, while showing the name for every product. (The C<&nbsp;>
will prevent blanked table cells if you use a border.)

=item [/item-change marker]

Companion to C<[item-change marker]>.

=item [matches]

Replaced with the range of match numbers displayed by the
search page. Looks something like "1-50". Make sure
you insert this item between a C<[more-list]> and C<[/more-list]>
element pair.

=item [more-list next_img* prev_img* page_img* border* border_current*]

Starts the section of the search page which is only displayed
if there are more matches than specified in C<mv_matchlimit>.
If there are less matches than the number in mv_matchlimit, all
text/html between the C<[more_list]> and C<[/more_list]> elements is
stripped.

Use in conjunction with the C<[more]> element to place pointers to
additional pages of matches.

If the optional arguments C<next_img>, C<prev_img>, and/or C<page_img>
are present, they represent image files that will be inserted instead
of the standard 'Next', 'Previous', and page number. If C<prev_img>
is C<none>, then no previous link will be output. If C<page_img> is
C<none>, then no links to pages of matches will be output. These are
URLs, are substituted for with I<ImageDir> and friends, and will be encased in
IMG tags. Lastly, C<border> is the border number to put.

In addition, if C<page_img> is used, it will be passed an argument of
the digit that is to be represented. This would allow an image generator
program to be used, generating page numbers on the fly.  The C<border>
and C<border_selected> values are integers indicating the border that
should be put around images in the C<page_img> selection. The <border_selected>
is used for the current page if set.

Examples:

C<[more-list next.gif prev.gif page_num.cgi 3]> causes anchors of:

     Previous   <IMG SRC="prev.gif" Border=3>
     Page 1     <IMG SRC="/cgi-bin/page_num.cgi?1">
     Page 2     <IMG SRC="/cgi-bin/page_num.cgi?2">
     Next       <IMG SRC="next.gif" Border=3>

C<[more-list next.gif prev.gif page_num.cgi]> causes anchors of:

     Previous   <IMG SRC="prev.gif">
     Page 1     <IMG SRC="/cgi-bin/page_num.cgi?1">
     Page 2     <IMG SRC="/cgi-bin/page_num.cgi?2">
     Next       <IMG SRC="next.gif">

C<[more-list next.gif prev.gif 0 0]> causes anchors of:


     Previous   <IMG SRC="prev.gif" Border=0>
     Page 1     <IMG SRC="/cgi-bin/page_num.cgi?1">
     Page 2     <IMG SRC="/cgi-bin/page_num.cgi?2">
     Next       <IMG SRC="next.gif" Border=0>

If you wish to set custom text for the "Previous" and "Next" usually
used, then you can define the next_img, prev_img, and page_img with
C<[next-anchor][/next-anchor]>, C<[prev-anchor][/prev-anchor]> and
C<[page-anchor][/page-anchor]>. The string $PAGE$ will be replaced with
the page number in the latter. The same example:


    [more-list 0 0 0]
    [next-anchor] Forward [/next-anchor]
    [prev-anchor] Back [/prev-anchor]
    [page-anchor] Page $PAGE$ [/page-anchor]
    [more]
    [/more-list]

will display U<Forward Page 1 Page 2 Back> for 2 pages.

As shown, you must pass a 0 for the arguments of each to
tell MiniVend to look for the assignments.

If you have many pages of matches and don't wish to have all
displayed at once, you can set C<[decade-next][/decade-next]>
and C<[decade-prev][/decade-prev]>. If you set them empty, a
search with 31 pages will display pages 21-30 like

  Previous 1 2 3 4 5 6 7 8 9 10 [more>>] Next

and pages 11-20 like:

  Previous [<<more] 11 12 13 14 15 16 17 18 19 20 [more>>] Next

If you set them to C<[decade-next](higher)[/decade-next]> and
C<[decade-prev](lower)[/decade-prev]> you will see:

  Previous (lower) 11 12 13 14 15 16 17 18 19 20 (higher) Next

Of course image-based anchors can be used as well.

=item [/more-list]

Companion to C<[more-list]>. 

=item [more]

Inserts a series of hyperlinks that will call up the next matches
in a series. They look like this:

    Previous 1 2 3 4 5 6 Next

The current page will not be a hyperlink. Every time the new
link is pressed, the list is re-built to correspond to the current
page. If there is no C<Next> or C<Previous> page, that link
will not be shown.

See the C<fr_resul.html> or C<search.html> files for examples. Make sure
you insert this item between a C<[more-list]> and C<[/more-list]> element pair.

=item [process-search]

Calls the search with the proper URL, including MiniVend session tags. Used
as the ACTION value for the search form.

=item [process-target frame]

Calls the search with the proper URL, including MiniVend session tags.
Used as the ACTION value for the search form if the results are to be
targeted to a different window than the one set by SearchFrame (which is
"_self" by default).

=back

=head1 THE ORDER PROCESS

MiniVend has a completely flexible order basket and checkout
scheme. The C<simple> demo presents a common use of this process,
in the directory pages/ord -- the files are:

    basket.html      The order basket displayed by default
    checkout.html    The form where the customer enters their billing
                     and shipping info
    receipt.html     The receipt displayed to the customer
    report.html      The order report mailed to you

It is not strictly necessary to display an order basket when
an item is ordered. If you specify a different page to be
displayed that is fine, but most customers will be confused if
you don't give them an indication that the order operation has
succeeded.

Any order basket is an HTML C<FORM>. It will have a number of
variables on it. At the minimum it must have an C<[item-list]> to
loop through the items, and the C<quantity> of each item must be
set in some place on that form. Any valid MiniVend tags may be
used on the page, and you may use multiple item lists if necessary.

=head2 How to set up an order link

On a product display page, use:

    [order 00-0011]Order the Mona Lisa[/order]

If coming from a search results or on-the-fly page, you may use the generated
C<[item-code]> thusly:

    [order [item-code]]Order [item-field name][/order]

Bear in mind that if you have not reached the page via a search or
on-the-fly operation, C<[item-code]> means nothing and will cause an error.

=head2 How to set up an order button

MiniVend can order via form submission as well. This allows you
to order a product (or group of products) via a form button. In its
simplest form, it is:

    <FORM ACTION="[process-target]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_todo ACTION=refresh>
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011">
    <INPUT TYPE=submit VALUE="Order the Mona Lisa">
    </FORM>

The default quantity is one.  An initial quantity may be set by the user
by adding an mv_order_quantity variable:

     Number to order:<INPUT TYPE=text NAME=mv_order_quantity VALUE="1">

You can order multiple items by stacking the variables:

    <FORM ACTION="[process-target]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_todo ACTION=refresh>
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011a">
    <INPUT TYPE=submit VALUE="Order the Mona Lisa with frame">
    </FORM>

Initial size or color may be set as well, provided I<UseModifier> is
set up properly:

    <INPUT TYPE=hidden NAME=mv_order_size VALUE="L">

If the order is coming from a generated flypage, loop list, or search
results page, you can get a canned select box from the 
C<[item-accessories size]> or C<[item-accessories size]> tag. See
I<Item Attributes>.

=head2 How to set up an on-the-fly item

If you enable the catalog directive I<OnFly>, setting it to the
name of a tag (possibly a UserTag) that can handle its calls, then
MiniVend will add items to the basket that are not in the product
database.

The C<OnFly> directive accepts a tag name:

    OnFly  onfly

MiniVend supplies the C<onfly> tag which will work with the descriptions
below. If your item code is not to be named C<mv_order_item> then you
must perform a rename in the C<Autoload> routine.

A basic link can be generated like:

    [area form="
            mv_todo=refresh
            mv_order_item=000101
            mv_order_fly=description=An on-the-fly item|price=100.01
    "]

The form parameter value C<mv_order_fly> can contain any number of fields
which will set corresponding parameters in the item attributes. The fields
are separated by the pipe (C<|>) character and contain value-parmeter
pairs separated by an = sign. (These are URL-encoded by the [area ...] tag,
of course.) You can set a size, color, or any other parameter. If you want
to see what the actual URL will look like, put the above example in a
page and study it.

The special attribute C<mv_price> can be used in conjunction with the
C<CommonAdjust> atom C<$> to set the price for checkout and display.

The C<[item-list]> sub-tag C<[item-description]>, when used with an
item-list, will use the item attribute C<description> to display in the 
basket.

If you wish to set up a UserTag to process on-the-fly items, it should
accept a call of

    usertag(mv_item_code, mv_item_quantity, mv_order_fly)

The C<mv_item_code> and C<mv_order_fly> parameters are required to trigger
MiniVend's C<add_item> routine (along with mv_todo=refresh to set the action).

The item will always act as if C<SeparateItems> or C<mv_separate_items> is
set.

Multiple items can be ordered at once by stacking the variables. If there
is only one C<mv_order_item> instance, however, you can stack the C<mv_order_fly>
variable so that all are concatenated together as with the C<|> symbol. So
the above example could be done as:

    [area form="
            mv_todo=refresh
            mv_order_item=000101
            mv_order_fly=description=An on-the-fly item
            mv_order_fly=price=100.00
    "]

Multiple items would need multiple instances of C<mv_order_item> with 
a corresponding C<mv_order_fly> for each C<mv_order_item>. You can
order both C<000101> and C<000101> as follows:

    [area form="
        mv_todo=refresh
 
        mv_order_item=000101
        mv_order_fly=description=An on-the-fly item|price=100.00
 
        mv_order_item=000102
        mv_order_fly=description=Another on-the-fly item|price=200.00
    "]

The following two forms correspond to the above two examples, in order,
with the slight refinement of adding a quantity:

  <FORM ACTION="[area process]" METHOD=POST>
        <INPUT TYPE=hidden NAME=mv_todo VALUE="refresh">
        <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.00">
        <INPUT TYPE=submit VALUE="Order button">
    </FORM>

   <FORM ACTION="[area process]" METHOD=POST>
        <INPUT TYPE=hidden NAME=mv_todo VALUE="refresh">
        <INPUT TYPE=hidden NAME=mv_order_item VALUE="000101">
        Qty: <INPUT SIZE=2 NAME=mv_order_quantity VALUE="1"><BR>
        <INPUT TYPE=hidden NAME=mv_order_fly
            VALUE="description=An on-the-fly item|price=100.00">
        <INPUT TYPE=hidden NAME=mv_order_item VALUE="000102">
        Qty: <INPUT SIZE=2 NAME=mv_order_quantity VALUE="1"><BR>
        <INPUT TYPE=hidden NAME=mv_order_fly
            VALUE="description=Another on-the-fly item|price=200.00">
        <INPUT TYPE=submit VALUE="Order two different with a button">
    </FORM>

=head2 Order Groups

MiniVend allows you to group items together, making a master item
and sub-items. This can be used to delete accessories or options when
the master item is deleted. In its simplest form, you order just one
master item and all subsequent items are sub-items.

    <FORM ACTION="[process-target]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_todo ACTION=refresh>
    <INPUT TYPE=hidden NAME=mv_order_group VALUE="1">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011a">
    <INPUT TYPE=submit VALUE="Order the Mona Lisa with frame">
    </FORM>

If you wish to stack more than one master item, then you must define
mv_order_group for B<all> items, with either a 1 value (master) or 0 value
(sub-item). A master owns all subsequent sub-items until the next master
is defined.

    <FORM ACTION="[process-target]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_todo ACTION=refresh>
    <INPUT TYPE=hidden NAME=mv_order_group VALUE="1">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011">
    <INPUT TYPE=hidden NAME=mv_order_group VALUE="0">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="00-0011a">
    <INPUT TYPE=hidden NAME=mv_order_group VALUE="1">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="19-202">
    <INPUT TYPE=hidden NAME=mv_order_group VALUE="0">
    <INPUT TYPE=hidden NAME=mv_order_item VALUE="99-102">
    <INPUT TYPE=submit VALUE="Order items">
    </FORM>

When the master item C<00-0011> is deleted from the basket,
C<00-0011a> will be deleted as well. And when 19-202 is deleted,
then 99-102 will be deleted from the basket.

NOTE: You cannot use checkboxes for this type of thing, for they
do not pass a value when unchecked.  Use radio groups or select/drop-down
buttons.

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

=head2 Basket display

The basket page(s) are where the items are tracked and adjusted by the
customer. It is possible to have an unlimited number of basket pages.
It is also possible to have multiple shopping carts, as in buy or
sell. This allows a basket/checkout type of ordering scheme, with custom
order pages for items which have many accessories.

The name of the page to display can be configured in several
ways:

=over 4

=item 1

Set the SpecialPage C<order> to the page to display
when an item is ordered.

=item 2

Set the FrameOrderPage directive to the page to use
when frames are enabled. This overrides option 1.

=item 3

Use the C<[order item cart/page] Order it! [/order]> form of
order tag to specify an arbitrary order page for an item.

=item 4

If already on an order page, set the mv_checkout, mv_orderpage,
mv_nextpage, mv_successpage, or mv_failpage variables.

=back 

The variables mentioned above modify the page display in the
following ways:

=over 4

=item mv_cartname

The shopping cart (default is main) to be used for this order
operation.

=item mv_checkout

Page to be displayed when checkout is called from the form

=item mv_failpage

Page to be displayed on a failed order
check (see I<Advanced Multi-level Order Pages>)

=item mv_nextpage

Page to display on a return operation.

=item mv_orderpage

Page to be displayed on a refresh.

=item mv_successpage

Page to be displayed on a successful order
check (see I<Advanced Multi-level Order Pages>).

=item mv_order_profile

Order profile to be used if the form action is F<submit>
(see I<Advanced Multi-level Order Pages>).

=back

=head2 Multiple Shopping Carts

You can maintain multiple shopping carts with MiniVend (2.02 and above).
One shopping cart -- main, by name -- is defined when the user session
starts. If the user orders item M1212 with the following tag:

    [order M1212 layaway] Order this item! [/order]

the order will be placed in the cart named I<layaway>. However, by default
you won't see what you want!  That is because the default shopping basket
page won't display the cart you are thinking it will -- it will show
the main cart. So copy the default cart (pages/ord/basket.html in the demos)
to a new file, insert a C<[cart layaway]> tag, and submit it as a MiniVend
page name addendum to the cart name:

    [order M1212 layaway/lay_basket] Order this item! [/order]

Now the contents of the I<layaway> cart will be displayed. If you need
to display a different price, you will have to emulate the C<[subtotal]>,
C<[item-price]>, C<[item-subtotal]>, etc. fields with C<[item-list]>, C<[calc]>, 
and C<[currency]> tags. This snippet emulates the item-price tag for
a different price field I<layaway-price>:

    [currency] [item-field layaway-price] [/currency]

An item subtotal:

    [currency]
        [calc]
            [item-field layaway-price] * [item-quantity]
        [/calc]
    [/currency]

A cart subtotal, using the item-list tag:

    [currency]
        [calc]
            [item-list layaway]
            ([item-field layaway-price] * [item-quantity]) +
            [/item-list]
            0
        [/calc]
    [/currency]

The zero is needed because of the trailing plus sign left by the
iterative C<[item-list]> tag.

Shipping and the C<[nitems]> tag will still work properly with a
different price.

You can also order items from a form, using the C<mv_order_item>,
C<mv_cartname>, and optional C<mv_order_quantity> variables.

 <FORM METHOD=POST ACTION="[process-order]">
 <input type=checkbox name="mv_order_item" value="M3243"> Item M3243
 <input name="mv_order_quantity" value="1"> Quantity
 <input type=hidden name="mv_cartname" value="layaway">
 <input type=hidden name="mv_doit" value="refresh">
 <input type=submit name="mv_junk" value="Place on Layaway Now!">
 </FORM>

=head2 Advanced Multi-level Order Pages

An unlimited number of order checking profiles can be defined with the
I<OrderProfile> directive, or by defining order profiles in scratch
variables. This allows a multi-level ordering process, with checking
for format and validity at every stage.

To custom-configure the error message, place it after the format check
requirement.

Specifications take the form of an order page variable (like name
or address), followed by an equals sign and one of five check types:

=over 4

=item required

A non-blank value is required

=item mandatory

Must be non-blank, and must have been specified on this
form, not a saved value from a previous form

=item phone

The field must look like a phone number, by a very
loose specification allowing numbers from all countries

=item phone_us

Must have US phone number formatting, with area code

=item state

Must be a US state, including DC and Puerto Rico.

=item province

Must be a Canadian province or pre-1997 territory.

=item state_or_province

Must be a US state or Canadian province.

=item zip

Must have US postal code formatting, with optional ZIP+4.
Also called by the alias C<us_postcode>.

=item ca_postcode

Must have Canadian postal code formatting. Checks for a valid
first letter.

=item postcode

Must have Canadian or US postal code formatting.

=item true

Field begins with B<y>, B<1>, or B<t> (Yes, 1, or True) - not case sensitive

=item false

Field begins with B<n>, B<0>, or B<f> (No, 0, or False) - not case sensitive

=item email

Rudimentary email address check, must have an '@' sign,
a name, and a minimal domain

=back

Also, there are pragmas that can be used to change behavior:

=over 4

=item &charge

Perform a real-time charge operation. If set to any value but
"custom", it will use MiniVend's CyberCash routines. To set to
something else, use the value "custom ROUTINE". The ROUTINE should
be a GlobalSub which will cause the charge operation to occur -- if
it returns non-blank, non-zero the profile will have succeeded. If
it returns 0 or undef or blank, the profile will return failure.

=item &credit_card

Checks the mv_credit_card_* variables for validity. If set to
"standard", it will use Minivend's C<encrypt_standard_cc> routines.
This destroys the CGI value of mv_credit_card_number -- if you don't
want that to happen (perhaps to save it for sending to CyberCash)
then add the word C<keep> on the end.

Example:

    # Checks credit card number and destroys number after encryption
    # The charge operation can never work
 
    &credit_card=standard
    &charge=custom authorizenet
  
    # Checks credit card number and keeps number after encryption
    # The charge operation can now work
 
    &credit_card=standard keep
    &charge=custom authorizenet

You can supply your own check routine with a GlobalSub:

    &credit_card=check_cc

The C<GlobalSub> check_cc will be used to check and encrypt the
credit card number, and its return value will be used to determine
profile success.

=item C<&>fail

Sets the mv_failpage value.

    &fail=page4

If the submit process succeeds, the user will be sent to the
page C<page4>.


=item C<&>fatal

Set to '&fatal=yes' if an error should generate
the error page.

=item C<&>final

Set to '&final=yes' if a successful check should cause the order to be placed.

=item C<&>return

Causes profile processing to terminate with either a success
or failure depending on what follows. If it is non-blank and
non-zero, the profile succeeds. 

    # Success :)
    &return 1

    # Failure :\
    &return 0

Will ignore the &fatal pragma, but &final is still in effect if set.

=item C<&>set

Set a user session variable to a value, i.e. C<&set=mv_email [value email]>.
This will not cause failure if blank or zero.

=item C<&>setcheck

Set a user session variable to a value, i.e. C<&set=mv_email [value
email]>.  This B<will> cause failure if set to a blank or zero. It is
usually placed at the end after a &fatal pragma would have caused the
process to stop if there was an error -- can also be used to determine
pass/fail based on a derived value, as it will cause failure if it
evaluates to zero or a blank value.

=item C<&>success

Sets the mv_successpage value. Example:

    &success=page5

If the submit process succeeds, the user will be sent to the
page C<page5>.

=back

As an added measure of control, the specification is evaluated for the
special MiniVend tags to provide conditional setting of order
parameters. With the C<[perl]> C<[/perl]> capability, quite complex checks
can be done. Also, the name of the page to be displayed on an error can
be set in the C<mv_failpage> variable.

The following file specifies a simple check of formatted parameters:

 name=required You must give us your name.
 address=required Oops! No address.
 city=required
 state=required
 zip=required
 email=required
 phone_day=phone_us XXX-XXX-XXXX phone-number for US or Canada
 &fatal=yes
 email=email Email address missing the domain?
 &set=mv_email [value email]
 &set=mv_successpage ord/shipping

The profile above only performs the &set directives if all of the
previous checks have passed -- the &fatal=yes will stop processing after
the check of the email address if any of the previous checks failed.

If you want to place multiple order profiles in the same file,
separate them with __END__, which must be on a line by itself.


User-defined check routines can be defined in a GlobalSub:

	GlobalSub <<EOF
	sub set_up_extra_check {
		BEGIN {
			package Vend::Order;
			sub _pt_postcode {
				# $ref is to Vend::Session->{'values'} hash
				# $var is the passed name of the variable
				# $val is current value of checked variable
				my($ref, $var, $val) = @_;
				
				if ($ref->{country} =~ /^(PT|portugal)$/i) {
					return $val =~ /^\d\d\d\d$/ ?  1 : 0;
				}
				else {
					return 1;
				}
			}
		}
	}
	EOF

Now you can specify in an order profile:

  postcode=pt_postcode

There must be an underscore preceding the routine name in this
case. Very elaborate checks are possible, of course. If some user
takes on the polyglot it would be appreciated if they contribute
the routines.

=head2 Simple Order Report File

The simple order report file, "report", defines the layout of the order
report which gets mailed on the completion of the order. For example,

              Order Date: $date

                    Name: $name
           Email address: $email

        Shipping address: $addr
        Town, State, Zip: $town, $state $zip
                 Country: $country

Any input field from the order page can be included using the dollar
sign notation.

To prevent a value from being included in the order report, just add it
to the B<ReportIgnore> configuration directive. 

MiniVend defines some values for use in the search form -- they begin
with C<mv_> and are automatically ignored.

=head2 Fully-configurable Order Reports

You can specify a fully-configurable order report by setting the hidden
field "mv_order_report" to a legal MiniVend page. This page will be
interpolated with all MiniVend tags before sending by email. The order
number as set by backend ordering is in the variable mv_order_number,
and available for your use.

You could if you wish include HTML in the file, which will be interpreted
by many mailers, but you can choose to use standard ASCII format.
An example report is provided in the demo file <pages/ord/report.html>.

=head2 Order Receipts

If you specify a legal MiniVend page name in the ReceiptPage directive,
the user will be sent this page instead of the default "confirmation"
file. The file can of course be configured with all MiniVend tags, and
will be interpolated for the ordered items before they are deleted from
the user record. If you want to have a different receipt for different
carts, set the C<mv_order_receipt> variable in the form.

=head2 The Order Counter

An order counter can be enabled if the I<OrderCounter> directive
is set to a 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 default
starting number at any time.

This capability is made possible by the File::CounterFile module,
available (as a part of the fine libwww modules) at the same place you
got MiniVend. It is included with the distribution.

=head2 Customer Input Fields

On the order (or shopping basket) page, by default order.html, you will
have a number of input fields allowing the customer to enter information
such as their name and address. You can add more fields simply by
putting more input elements on the order.html page, and the information
will automatically be included in the order report. Input elements
should be written in this way:

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

Choose a name for this input field such as "email" for an email
address. Set the name attribute to the name you have chosen.

The value attribute specifies the default value to give the field when
the page is displayed. Because the customer may enter information on
the order page, return to browsing, and come back to the order page,
you want the default value to be what was entered the first time. This
is done with the C<[value var]> element, which returns the last value of an
input field. Thus,

    value="[value name]"

will evaluate to the name entered on the previous order screen, such
as:

       value="Jane Smith"

which will be displayed by the browser.

The size attributes specifies how many characters wide the input field
should be on the browser. You do not need to set this to fit the
longest possible value since the browser will scroll the field, but
you should set it large enough to be comfortable for the customer.

=head1 PRODUCT PRICING

MiniVend maintains a price in its database for every product. The price
field is the one required field in the product database -- it is necessary
to build the price routines.

For speed, MiniVend builds the code that is used to determine a product's
price at catalog configuration time. If you choose to change a directive
that affects product pricing you must reconfigure the catalog.

=head2 Simple pricing

The simplest method is flat pricing based on a fixed value in
the C<products> database. If you put that price in a field named
C<price>, you don't need to do more. If you want to change pricing
based on quantity, size, color or other factors read on.

=head2 Price Maintenance with CommonAdjust

As of MiniVend 3.11, a flexible chained pricing scheme is available
when the I<CommonAdjust> directive is set.

NOTE: For compatibility with older carts, if both PriceAdjustment
and CommonAdjust are set, and CommonAdjust contains a valid database
identifier, the CommonAdjust value is used to set pricing adjustments
based on item attributes. This is not discussed further in this
section; all items below assume I<PriceAdjustment> is not in use.

We talk below about a I<CommonAdjust string>; it will be defined
in due time.

A few rules about CommonAdjust, all assuming the I<PriceField> directive
is set to C<price>:

=over 4

=item 1

If C<CommonAdjust> is set to any value, a valid I<CommonAdjust string> or
not, extended price adjustments are enabled. It may also hold the
default pricing scheme.

=item 2

The C<price> field may also hold a I<CommonAdjust string>. It takes
precedence over the default.

=item 3

If the value of the C<CommonAdjust> directive is set to a CommonAdjust
string, and the C<price> field is empty or specifically I<0>, then it
will be used to set the price of the items.

=item 4

If PriceBreaks is in use, it's price will take precedence over the
value of C<CommonAdjust>, though it may also contain a CommonAdjust string.

=item 5

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

=item 6

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

=back

Prices may be adjusted in several ways, and the individual actions
are referred to below as I<atoms>.  Price atoms they may be I<final>,
I<chained>, or I<fallback>. A final price atom is always applied if it
does not evaluate to zero. A chained price atom is subject to further
adjustment. A fallback price atom is skipped if a previous chained price
was not zero.

Atoms are separated by whitespace, and may be quoted (although there
should not normally be whitespace in a setting). A chained item ends
with a comma.  A fallback item has a leading semi-colon.  Final atoms
have no comma appended or semi-colon prepended.

A I<settor> is the means by which the price is set. There are
There are eight different types of price settors. All settors
can then yield another CommonAdjust string. 

It is quite possible to create endless loops, so the maximum
number of initial CommonAdjust strings is set to 16, and there
may be only 20 iterations before the price will return zero on
an error.

B<NOTE>:  Common needs are easily shown but not so easily explained;
skip to the examples if the reference below if your vision starts to
blur when reading the next section. 8-)

USAGE: Optional items below have asterisks appended. The asterisk should
not be used in the actual string. Optional B<base> or B<table> always
defaults to the active C<products> database table.  The optional B<key>
defaults to the item code except in a special case for the attribute-based
lookup. The B<field> name is not optional except in the case of an
attribute lookup.

=over 4

=item N.NN or -N.NN

where N is a digit.  A number which is applied directly; for instance
10 will yield a price of 10. May be a positive or negative number.

=item N.NN%

where N is a digit.  A number which is applied as a percentage of
the I<current> price value. May be a positive or negative number. For
example, if the price is 10 and -8% is applied, the next price value
will be 9.20.

=item table*:column:key*

Causes a straight lookup in a database table. The optional B<table> 
defaults to the main products database table for the item (subject
of course to multiple product files). The B<column> must always
be present. The optional B<key> defaults to the item code except
in a special case for the attribute-based lookup. The return value
is then re-parsed as another price settor.

=item table*:col1..col5,col10:key*

Causes a quantity lookup in database table B<table> (which defaults to
the products database), with a set of comma-separated fields, looked
up by the optional B<key>. (Key defaults to the item code, of course).
If ranges are specified with .., each column in the sequence will be used;
Therefore

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

is the same as

    pricing:p1..p5,p10:

Leading non-digits are stripped, and the item quantity is compared with
the numerical portion of the column name. The price is set to the value of
the database column (numeric portion) that is at least equal to it but
doesn't yet reach the next break.

WARNING: If the field at the appropriate quantity level is blank,
a zero cost will be returned from the atom. It is important to
have all columns populated.

=item ==attribute:table*:column*:key*

Does an attribute-based adjustment. The attribute is looked up in the
database B<table>, with the optional B<column> defaulting to the same
name as the I<value> of the B<attribute>. If the column is not left blank,
the I<key> is set to the I<value> of the B<attribute> if blank.

=item & CODE

The leading C<&> sign is stripped and the code is passed to the
equivalent of a C<[calc]> tag. No MiniVend tags can be used, but
the &tag_data routine is available, the current value of the price
and quantity are available as C<$s>, and the current item (code, quantity,
price, and any attributes) are available as C<$item>, all forced
to the package Vend::Interpolate. That means that in a UserTag:

  $Vend::Interpolate::item          is the current item
  $Vend::Interpolate::item->{code}  gives key for current item
  $Vend::Interpolate::item->{size}  gives size for current item (if there)
  $Vend::Interpolate::item->{mv_ib} gives database ordered from

=item [valid minivend tags]

If the settor begins with a square bracket (C<[>) or underscore, it
is parsed for MiniVend tags with variable substitution (but no Locale
substitution). You may define a price in a I<Variable> in this fashion.
The string is re-submitted as an atom, so it may yield yet another 
settor.

=item $

Tells MiniVend to look in the C<mv_price> attribute of the shopping cart,
and apply that price as the final price, if it exists. The attribute must
be a numerical value.

=item >>word

Tells the routine to return C<word> directly as the result. This is not
useful in pricing, as it will evaluate to zero. But when CommonAdjust
is used for shipping, it is a way of re-directing shipping modes.

=item word

The value of C<word>, which must not match any of the other settors,
is available as a key for the next lookup (only). If the next settor
is a database lookup, and it contains a dollar sign (C<$>) the C<word>
will be substituted; i.e. C<table:column:$> becomes C<table:column:word>.

=item ( settor )

The value returned by C<settor> will be used as a key for the next
lookup, as above.

=back

=head2 CommonAdjust Examples

Most examples below use an outboard database table named B<pricing>, but
any valid table including the B<products> table can be used. We will refer
to this B<pricing> table:

  code    common  q1     q5     q10    XL    S      red
  99-102          10     9      8      1     -0.50  0.75
  00-343                               2
  red      0.75

The simplest case is a straight lookup on an attribute; I<size> in this
case. 

  10.00, ==size:pricing

With this value in the C<price> field, a base price of 10.00 will be
adjusted with the value of the I<size> attribute. If size for the item
99-102 is set to C<XL> then 1.00 will be added for a total price of 11.00;
if it is C<S> then .50 will be subtracted for a total price of 9.50;
for any other value of I<size> no further adjustment would be made. 00-343
would be adjusted up 2.00 only for I<XL>.

  10.00, ==size:pricing, ==color:pricing

This is the same as above, except both size and color are adjusted for.
A color value of red for item code 99-102 would add 0.75 to the price. For
00-343 it would have no effect.

  10.00, ==size:pricing, ==color:pricing:common

Here price is set based on a common column, keyed by the value of the
color attribute. Any item with a color value of red would have 0.75 added
to the base price.

  pricing:q1,q5,q10:, ;10.00, ==size:pricing, ==color:pricing:common

Here is a quantity price lookup, with a fallback price setting. If there
is a valid price found at the quantity of 1, 5, or 10, depending on
item quantity, then it will be used. The fallback of 10.00 only applies if no
non-zero/non-blank price was found at the quantity lookup. In either
case, size/color adjustment is applied.

  pricing:q1,q5,q10:, ;10.00 ==size:pricing, ==color:pricing:common

Removing the comma from the end of the fallback string stops color/size
lookup if it reaches that point. If a quantity price was found, then size
and color are chained.

  pricing:q1,q5,q10:, ;products:list_price, ==size:pricing, ==color:pricing

The value of the database column C<list_price> is used as a fallback
instead of the fixed 10.00 value. The above value might be a nice one
to use as the default for a typical retail catalog that has items with
colors and sizes.

=head2 PriceBreaks, discounts, and PriceAdjustment

There are several ways that MiniVend can modify the price of a product during 
normal catalog operation. Several of them require that the I<pricing.asc>
file be present, and that you define a pricing database. You do that by
placing the following directive in I<catalog.cfg>:

  Database  pricing pricing.asc 1

NOTE: PriceAdjustment is slightly deprecated by CommonAdjust, but
will remain in use at least through the end of Version 3 of MiniVend.

Configurable directives and tags with regard to pricing:

=over 4

=item *

Quantity price breaks are configured by means of the I<PriceBreaks> and
I<MixMatch> directives. They require a field named specifically C<price>
in the pricing database. The B<price> field contains a space-separated
list of prices that correspond to the quantity levels defined in the
F<PriceBreaks> directive. If quantity is to be applied to all items in
the shopping cart (as opposed to quantity of just that item) then the
I<MixMatch> directive should be set to B<Yes>.

=item *

Individual line-item prices can be adjusted according to the value of
their attributes. See I<PriceAdjustment> and I<CommonAdjust>. The
pricing database B<must> be defined unless you define the F<CommonAdjust>
behavior.

=item *

Product discounts for individual products, specific product codes,
all products, or the entire order can be configured with the C<[discount ...]>
tag. Discounts are applied on a per-user basis -- you can gate the
discount based on membership in a club or other arbitrary means. See
I<Product Discounts>.

=back

For example, if you decided to adjust the price of T-shirt part number
99-102 up 1.00 when the size is extra large and down 1.00 when the size is small,
you would have the following directives defined in <catalog.cfg>:

  Database          pricing pricing.asc 1
  UseModifier       size
  PriceAdjustment   size

To enable automatic modifier handling, you define a size field in
products.txt:

  code    description   price    size
  99-102  T-Shirt       10.00    S=Small, M=Medium, L=Large*, XL=Extra Large

You would place the proper tag within your C<[item-list]> on the shopping-basket
or order page:

    [item-accessories size]

In the pricing.asc database source, you would need:

  code      S       XL
  99-102    -1.00   1.00

As of MiniVend 3.06, if you want to assign a price based on the option,
precede the number with an equals sign:

  code    S       M       L       XL
  99-102  =9.00   =10     =10     =11

IMPORTANT NOTE: Price adjustments occur AFTER quantity price breaks, so
the above would negate anything set with the I<PriceBreaks> directive/option.

Numbers that begin with an equals sign (C<=>) are used as absolute
prices and are I<interpolated for MiniVend tags first>, so you can
use subroutines to set the price. To facilite coordination with the
subroutine, the session variables C<item_code> and C<item_quantity> are
set to the code and quantity of the item being evaluated. They would
be accessed in a global subroutine with C<$Vend::Session->>C<{item_code}>
and C<$Vend::Session->>C<{item_quantity}>.

The pricing information must always come from a database because
of security.

=head2 Item Attributes

MiniVend allows item attributes to be set for each ordered item. This
allows a size, color, or other modifier to be attached to a common
part number. If multiple attributes are set, then they should be
separated by commas. Previous attribute values can be saved by means
of a hidden field on a form, and multiple attributes for each item
can be I<stacked> on top of each other.

The configuration file directive I<UseModifier> is used to set
the name of the modifier or modifiers. For example

    UseModifier        size,color

will attach both a size and color attribute to each item code that
is ordered.

B<IMPORTANT NOTE:> You may not use the following names for attributes:

    item  group  quantity  code  mv_ib  mv_mi  mv_si

You can also set it in scratch with the mv_UseModifier
scratch variable -- C<[set mv_UseModifier]size color[/set]> has the
same effect as above. This allows multiple options to be set for
products. Whichever one is in effect at order time will be used.
Be careful, you cannot set it more than once on the same page.
Setting the C<mv_separate_items> or global directive I<SeparateItems>
places each ordered item on a separate line, simplifying attribute
handling. The scratch setting for C<mv_separate_items> has the same
effect.

The modifier value is accessed in the C<[item-list]> loop with the
C<[item-modifier attribute]> tag, and form input fields are placed with the
C<[modifier-name attribute]> tag. This is similar to the way that quantity
is handled, except that attributes can be "stacked" by setting multiple
values in an input form.

You cannot define a modifier name of I<code> or I<quantity>, as they
are already used. You must be sure that no fields in your forms
have digits appended to their names if the variable is the same name
as the attribute name you select, as the C<[modifier-name size]> variables
will be placed in the user session as the form variables size0, size1,
size2, etc. 

You can use the C<[loop arg="item item item"]> list to reference multiple display
or selection fields for modifiers (in MiniVend 3.0, you can have it
automatically generated --see below). 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 this
would always be contained within the C<[item_list]> C<[/item-list]>
pair.

    <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 combined
with the C<[checked ...]> tag.

MiniVend 3.0 will automatically generate the above select box
when the C<[accessories <code> size]> or C<[item-accessories size]>
tags are called. They have the syntax:

   [item_accessories attribute*, type*, field*, database*, name*, outboard*]
  
   [accessories code attribute*, type*, field*, database*, name*, outboard*]

=over 4

=item code

Not needed for item-accessories, this is the product code of the item to
reference.
 
=item attribute

The item attribute as specified in the UseModifier configuration
directive. Typical are C<size> or C<color>.

=item type

The action to be taken. One of:

  select          Builds a dropdown <SELECT> menu for the attribute.
                  NOTE: This is the default.
 
  multiple        Builds a multiple dropdown <SELECT> menu for the
                  attribute.  The size is equal to the number of
                  option choices.
                   
  display         Shows the label text for *only the selected option*.
   
  show            Shows the option choices (no labels) for the option.
   
  radio           Builds a radio box group for the item, with spaces
                  separating the elements.
                   
  radio nbsp      Builds a radio box group for the item, with &nbsp;
                  separating the elements.
                   
  radio left n    Builds a radio box group for the item, inside a
                  table, with the checkbox on the left side. If "n"
                  is present and is a digit from 2 to 9, it will align
                  the options in that many columns.
                   
  radio right n   Builds a radio box group for the item, inside a
                  table, with the checkbox on the right side. If "n"
                  is present and is a digit from 2 to 9, it will align
                  the options in that many columns.

   
  check           Builds a checkbox group for the item, with spaces
                  separating the elements.
                   
  check nbsp      Builds a checkbox group for the item, with &nbsp;
                  separating the elements.
                   
  check left n    Builds a checkbox group for the item, inside a
                  table, with the checkbox on the left side. If "n"
                  is present and is a digit from 2 to 9, it will align
                  the options in that many columns.
                   
  check right n   Builds a checkbox group for the item, inside a
                  table, with the checkbox on the right side. If "n"
                  is present and is a digit from 2 to 9, it will align
                  the options in that many columns.

The default is 'select', which builds an HTML select form entry for
the attribute.  Also recognized is 'multiple', which generates a
multiple-selection drop down list, 'show', which shows the list of
possible attributes, and 'display', which shows the label text for the
selected option only.

=item field

The database field name to be used to build the entry (usually a field
in the products database).  Defaults to a field named the same as the
attribute.

=item database

The database to find B<field> in, defaults to the first products file
where the item code is found.

=item name

Name of the form variable to use if a form is being built. Defaults to
mv_order_B<attribute> -- i.e.  if the attribute is B<size>, the form
variable will be named B<mv_order_size>.

=item outboard

If calling the item-accessories tag, and you wish to select from an
outboard database, you can pass the key to use to find the accessory
data.

=back

When called with an attribute, the database is consulted and looks for
a comma-separated list of attribute options. They take the form:

    name=Label Text, name=Label Text*

The label text is optional -- if none is given, the B<name> will
be used.

If an asterisk is the last character of the label text, the item is
the default selection. If no default is specified, the first will be
the default. An example:

    [item_accessories color]

This will search the product database for a field named "color". If
an entry "beige=Almond, gold=Harvest Gold, White*, green=Avocado" is found,
a select box like this will be built:

    <SELECT NAME="mv_order_color">
    <OPTION VALUE="beige">Almond
    <OPTION VALUE="gold">Harvest Gold
    <OPTION SELECTED>White
    <OPTION VALUE="green">Avocado
    </SELECT>

In combination with the C<mv_order_item> and C<mv_order_quantity> variables
this can be used to allow entry of an attribute at time of order.

If used in an item list, and the user has changed the value, the generated
select box will automatically retain the current value the user has selected.

The value can then be displayed with C<[item-modifier size]> on the
order report, order receipt, or any other page containing an
C<[item-list]>. 

=head2 Product Discounts

Product discounts can be set upon display of any page. The discounts
apply only to the customer receiving them, and are of one of three types:

    1. A discount for one particular item code (key is the item-code)
    2. A discount applying to all item codes (key is ALL_ITEMS)
    3. A discount for an individual line item (set the mv_disount attribute
       with embedded Perl)
    4. A discount applied after all items are totaled
       (key is ENTIRE_ORDER)

The discounts are specified via a formula. The formula is scanned for
the variables $q and $s, which are substituted for with the item
I<quantity> and I<subtotal> respectively. The variable $s is saved between
iterations, so the discounts are cumulative. In the case of the item and
all items discount, the formula must evaluate to a new subtotal for all
items I<of that code> that are ordered. The discount for the entire
order is applied to the entire order, and would normally be a monetary
amount to subtract or a flat percentage discount.

Discounts are applied to the effective price of the product, including
any quantity discounts or price adjustments.

To apply a straight 20% discount to all items:

    [discount ALL_ITEMS] $s * .8 [/discount]

or with named attributes:

    [discount code=ALL_ITEMS] $s * .8 [/discount]

To take 25% off of only item 00-342:

    [discount 00-342] $s * .75 [/discount]

To subtract $5.00 from the customer's order:

    [discount ENTIRE_ORDER] $s - 5 [/discount]

To reset a discount, set it to the empty string: 

    [discount ALL_ITEMS][/discount]

Perl code can be used to apply the discounts, and variables are
saved between items and are shared with the C<[calc]> tag. This
example gives 10% off if two items are ordered, with 5% more for 
each additional up to a maximum of 30% discount:

    [calc] 
        [item-list]
            $totalq{"[item-code]"} += [item-quantity];
        [/item-list]
            return '';
    [/calc]
    
    [item-list]
        [discount code="[item-code]"]
            return ($s)       if $totalq{"[item-code]"} == 1;
            return ($s * .70) if $totalq{"[item-code]"} > 6;
            return ($s * ( 1 - 0.05 * $totalq{"[item-code]"} ));
        [/discount]
    [/item-list]

Here is an example of a special discount for item code 00-343 which prices
the I<second> one ordered at 1 cent:

    [discount 00-343]
    return $s if $q == 1;
    my $p = $s/$q;
    my $t = ($q - 1) * $p;
    $t .= 0.01;
    return $t;
    [/discount]

If you want to display the discount amount, use the C<[item-discount]> tag.

    [item-list]
    Discount for [item-code]: [item-discount]
    [/item-list]

Finally, if you want to display the discounted subtotal, you need to
use the C<[calc]> capability:

    [item-list]
    Discounted subtotal for [item-code]: [currency][calc]
                                            [item-price] * [item-quantity]
                                            [/calc][/currency]
    [/item-list]

=head2 Sales Tax

MiniVend allows calculation of sales tax on a straight percentage basis,
with certain items allowed to be tax-exempt. To enable this feature,
the directive I<SalesTax> is initialized with the name of a field (or
fields) on the order form. Commonly, this is zipcode and/or state:

    SalesTax    zip,state

This being done, MiniVend assumes the presence of a file F<salestax.asc>,
which contains a database with the percentages. Each line of 
F<salestax.asc> should be a code (again, usually a five-digit zip or
a two letter state) followed by a tab, then a percentage. Example:

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

Based on the user's entry of information in the order form, MiniVend
will look up (for our example SalesTax directive) first the zip, then
the state, and apply the percentage to the SUBTOTAL of the order. The
subtotal will include any taxable items, and will also include the
shipping cost if the state/zip is included in the I<TaxShipping> directive.
It will add the percentage, then make that available with the C<[salestax]>
tag for display on the order form. If no match is found, the entry
'default' is applied -- that is normally 0, but can be anything.

If business is being done on a national basis, it is now common to have
to collect sales tax for multiple states. If you are doing so, it is possible
to subscribe to a service which issues regular updates of the sales tax
percentages -- usually by quarterly or monthly subscription. Such a
database should be easily converted to MiniVend format -- but some systems
are rather convoluted, and it will be well to check and see if the
program can export to a flat ASCII file format based on zip code.

If some items are not taxable, then you must set up a field in your
database which indicates that. You then place the B<name> of that field
in the I<NonTaxableField> directive. If the field for that item
evaluates true on a yes-no basis (i.e. is set to C<yes>, C<y>, 1, or the
like), sales tax will not be applied to the item. If it evaluates false,
it will be taxed.

If your state taxes shipping, use the I<TaxShipping> directive.
Utah and Nevada are known to tax shipping -- there may be others.

If you want to set a fixed tax for all orders, as might occur for VAT
in some countries, just set the I<SalesTax> directive to a value like
C<tax_code>, and define a variable in the user session to reflect the
proper entry in the C<salestax.asc> file.  To set it to 15% with the
above C<salestax.asc> file, you would put in a form:

    <INPUT TYPE=hidden NAME=tax_code VALUE="VAT">

or to do it without submitting a form:

    [perl] $Values->{tax_code} = 'VAT'; return; [/perl]

=head2 Using CyberCash

MiniVend will directly interface with Perl libraries provided by
CyberCash, Inc. This allows direct card billing at the time the order
is placed. If using CyberCash 2, the module is called CCLib.pm and its
version should be 1.3 or greater.  If the library is found at startup,
the message "CyberCash 2 module found" will be displayed.

MiniVend 3.11 and above support CyberCash 3; see the section below for
differences when using CyberCash 3.

To use CyberCash 2, you must first have a CyberCash Secure Merchant Payment
Server (SMPS) running on your system.  It must be fully enabled, and
should be tested OK with the CyberCash test suite.  This capability has
been tested to work with SMPS-2.1.x in C<mauthcapture> and C<mauthonly>
modes. Any modes supported by CyberCash should work.

The special mode C<minivend_test> will cause the transaction to complete
successfully and the information that would have been sent to the
CyberCash server to be logged in the catalog C<error.log> file.

The CyberCash Perl library module must be available to MiniVend -- if you
cannot install it in the main Perl library then the Minivend software
directory I<lib/> will suffice.  If it is found at MiniVend startup,
a message will be displayed.

MiniVend only will charge CyberCash in the final phase of the order
process, i.e. at the time the receipt and order report are generated.
The full amount as shown by the C<[total-cost]> tag will be billed --
if you need to do partial charges you will have to manage multiple
shopping carts.

The process of enabling CyberCash processing is something like this:

=over 4

=item 1

Turn off CreditCardAuto by setting the catalog directive to No. This
would normally be done right in catalog.cfg, but it also can be done in a
mv_click subroutine if you wish to mix transaction types:

    <INPUT TYPE=checkbox NAME=mv_click VALUE=CyberCash> Use CyberCash
    [set CyberCash]
        [perl]
            $Config->{CreditCardAuto} = 0;
            $Config->{CyberCash} = 1;
            return '';
        [/perl]
    mv_cyber_mode=mauthcapture
    [/set]

=item 2 

Enable the CyberCash directive in catalog.cfg (or with the technique
above). Also set the catalog I<Variable> value B<CYBER_SECRET> to the
"secret" for your payment server.  If you are not using the default
values of B<localhost> and B<8000> for your server and port, set
the Variables B<CYBER_HOST> and B<CYBER_PORT> as well.  In the catalog.cfg
file it would look like:

    CreditCardAuto   No
    CyberCash        Yes
    Variable         CYBER_SECRET  first-natl
    Variable         CYBER_PORT    8000
    Variable         CYBER_HOST    localhost

=item 3

Set your final order screen to accept the user form fields
C<mv_credit_card_number> (contains the actual card number),
C<mv_credit_card_exp_month> and C<mv_credit_card_exp_year> (the
expiration date month and year), and the fields containing name,
address, city, state, and country. And B<you must> define the form
field C<mv_cyber_mode> B<on the submitting form> to enable the
processing.

The order mode must be final, either by omitting an order profile
entirely or by defining an order profile that contains &final=yes.

=back

The fields containing name and address information should be
the same as on the standard MiniVend demo order pages:

    b_name       Billing name      takes priority
    name         Shipping name     used if b_name empty
    b_address    Billing address   takes priority
    address      Shipping address  used if b_address empty
    b_city       Billing city      takes priority
    city         Shipping city     used if b_city empty
    b_state      Billing state     takes priority
    state        Shipping state    used if b_state empty
    b_country    Billing country   takes priority
    country      Shipping country  used if b_country empty

If you must use other values, they can be redefined in catalog.cfg
with the I<Variable> B<CYBER_REMAP> like so:

    Variable CYBER_REMAP  name=my_name address=my_address

or like so:

    Variable <<EOF
    CYBER_REMAP
    b_name   my_bname
    name     my_name
    address  processed_address
    city     parsed_city
    EOF

B<NOTE:> As always when using the <<EOF (here document) capability, the
EOF must be on a line by itself, with no leading or trailing white space.
That includes carriage returns, Windows devotees. Upload in ASCII mode!

If you have defined the directive I<EncryptProgram> to be something
containing the value I<pgp>, then the I<CreditCardAuto> method will be
used to encrypt the mv_credit_card_number value before it is wiped
from memory. (Errors in that process will be silently ignored.) It
will never be written to the user session, at least by MiniVend
itself, so attempts to recall it on future forms will be in vain.

If the authorization fails, the special page I<failed> will be
displayed, and passed the CyberCash error message for display with
the C<[subject]> tag.  The order will not complete, i.e. the cart will
still be intact and no receipt or order report will be generated.
The error itself is always available as C<[data session cybercash_error]>.

If successful, the receipt page will be displayed, the order report
emailed, and the cart will be emptied.  If you wish to display the
order-id returned from CyberCash on the receipt, it is available in
C<[data session cybercash_id]>. If the order is successful, but is
detected as a "success-duplicate", C<[data session cybercash_error]>
will contain the message returned from CyberCash.

=head2 CyberCash 3.2

MiniVend 3.11 supports CyberCash 3 with the CCMckDirectLib3_2.pm
CCMckLib3_2.pm modules. If those modules are in place in your Perl
include path (you can use VendRoot/lib as a home if you wish), using
CyberCash 3.2 is the same as with CyberCash 2 server except:

=over 4

=item 1

CyberCash 3 maintains a configuration file (merchant.conf) for each
merchant. This needs to be set in the variable CYBER_CONFIGFILE
as in:

  Variable CYBER_CONFIGFILE  /home/you/mck-lib/conf/merchant.conf

In all likelihood, this will make the values of CYBER_HOST, CYBER_PORT,
and CYBER_SECRET moot. It will not hurt anything if they remain set.

=item 2 

The variables CYBER_VERSION needs to be set to a numeric value
of 3 or greater:

  Variable CYBER_VERSION  3.2

This allows CyberCash 2 and 3 implementations to coexist on the same machine.

=back

=head1 SORTING

MiniVend has standard sorting options for sorting the search lists,
loop lists, and item lists based on the contents of database fields. In
addition, adds list slices for limiting the displayed entries based on a
start value and chunk size (or start and end value, from which a chunk
size is determined). All accept a standard format sort tag which must
be directly after the list call:

    [loop 4 3 2 1]
    [sort -2 +2]
        [loop-code]
    [/loop]
 
    [search-list]
    [sort products:category:f]
        [item-price] [item-description]<BR>
    [/search-list]
 
    [item-list]
    [sort products:price:rn]
        [item-price] [item-code]<BR>
    [/item-list]
 
    [loop search="ra=yes"]
    [sort products:category products:title]
    [loop-field category] [loop-field title] <BR>
    [/loop]

All sort situations -- C<[search list]> , C<[loop list]> , C<[tag each table]>, and
C<[item-list]>, take options of the form:

  [sort database:field:option* -n +n =n-n ... ]


=over 4

=item database

The MiniVend database identifier.  This must be supplied, and should
normally be 'products' if you are using the default name for the database.

=item field

The field (column) of the database to be sorted on.

=item option

None, any, or combinations of the options:

  f   case-insensitive sort (folded) (mutually exclusive of n)
  n   numeric order (mutually exclusive of f)
  r   reverse sort

=item -n

The starting point of the list to be displayed, beginning at 1 for
the first entry.

=item +n

The number of entries to display in this list segment.

=item =n-n

The starting and ending point of the list display -- this is an
alternative to C<-n> and +n. You should specify in only one form -- if
both are specified the last one will take effect.

=item ...

Don't really put C<...> in. This just means that you may specify as
many sort levels as you wish. Lots of sort levels with large databases
will be quite slow.

=back

Multiple levels of sort are supported, and you can cross database
boundaries on different sort levels. Cross-database sorts on the same
level are not supported, so if you use multiple product databases you
will have to sort with embedded Perl. This is actually a feature in
some cases, since you can then display all items in a C<used> database
before or after your new ones in C<products>.

Examples, all based on the C<simple> demo:

=over 4

=item Loop list

    [loop 00-0011 19-202 34-101 99-102]
    [sort products:title]
        [loop-code] [loop-field title]<BR>
    [/loop]
 
Will display:

    34-101 Family Portrait
    00-0011 Mona Lisa
    19-202 Radioactive Cats
    99-102 The Art Store T-Shirt

If you instead do:

    [loop 00-0011 19-202 34-101 99-102]
    [sort products:title -3 +2]
        [loop-code] [loop-field title]<BR>
    [/loop]
 
you will see:

    19-202 Radioactive Cats
    99-102 The Art Store T-Shirt

The tag C<[sort products:title =3-4]> is equivalent to the
above.

=item Search list

A search of all products (i.e. http://yoursystem.com/cgi-bin/simple/scan/ra=yes):

    [search-list]
    [sort products:artist products:title:rf]
        [item-field artist] [item-field title]<BR>
    [/search-list]
 
will display:

    Gilded Frame
    Grant Wood American Gothic
    Jean Langan Family Portrait
    Leonardo Da Vinci Mona Lisa
    Salvador Dali Persistence of Memory
    Sandy Skoglund Radioactive Cats
    The Art Store The Art Store T-Shirt
    Vincent Van Gogh The Starry Night
    Vincent Van Gogh Sunflowers

Note the reversed order of the title for I<Van Gogh>, and the presence of
the accessory item I<Gilded Frame> at the front of the list (it has no
artist field, and as such sorts first).

Adding a slice option:

    [search-list]
    [sort products:artist products:title:rf =6-10]
        [item-field artist] [item-field title]<BR>
    [/search-list]
 
will display:

    Sandy Skoglund Radioactive Cats
    The Art Store The Art Store T-Shirt
    Vincent Van Gogh The Starry Night
    Vincent Van Gogh Sunflowers

If the end value/chunk size exceeds the size of the list, only the
elements that exist will be displayed, starting from the start value.

=item Shopping cart

    [item-list]
    [sort products:price:rn]
        [item-price] [item-code]<BR>
    [/item-list]
 
will display the items in your shopping cart sorted on their price, with
the most expensive shown first. (Note that this is based on the database field,
and doesn't take quantity price breaks or discounts into effect.) B<NOTE:> You 
cannot sort on modifier values or quantities.

=item Complete database contents

    [tag each products]
    [sort products:category products:title]
    [loop-field category] [loop-field title] <BR>
    [/tag]

A two level sort, that will sort products based first on their category
then on their title within the category.

=back

Note that large lists may take some time to sort -- if you have a product
database with many thousands of items, it is not recommended that you
use the C<[tag each products]> sort unless you are planning on caching or
statically building your pages.

=head1 SHIPPING

MiniVend has a powerful custom shipping facility that performs
UPS and other shipper lookups, as well as a flexible rule-based
facility for figuring cost by other methods.

=head2 Shipping Cost Database

The shipping cost database (located in ProductDir/shipping.asc) is a
tab-separated ASCII file with six fields -- code, text description,
criteria (quantity or weight, for example), minimum number, maximum
number, and cost. None of the fields are case-sensitive.

If you want to define the shipping database in your catalog configuration
file, set the Variable MV_SHIPPING to what would be its contents.

To set the file to be something other than shipping.asc in the products
directory, set the C<Special> directive:

    Special  shipping.asc  /home/user/somewhere/shipping_defs

There are two styles of setting, which can be mixed in the same file. The
first is line-based, and expects 6 or more TAB-separated fields. They
would look like:

default No shipping weight  0   99999999    0

upsg    UPS Ground  weight  0   0   e Nothing to ship!
upsg    UPS Ground  weight  0   150 u Ground [default zip 98366] 3.00
upsg    UPS Ground  weight  150 999999  e @@TOTAL@@ lbs too heavy for UPS

The second is a freeform method, with a C<mode: Description text> introducing
the mode line, and the special encoding called out by indented parameters.
The below is identical to the above:

    upsg: UPS Ground
        criteria    weight
        min         0
        max         0
        cost        e Nothing to ship!
        
        min         0
        max         150
        cost        u
        table       2ndDayAir
        geo         zip
        default_geo 98366
        adder       3

        min         150
        max         999999
        cost        e @@TOTAL@@ lbs too heavy for UPS

The second format has several advantages. You can span multiple lines
with the <<HERE document format, like so:

    upsg: UPS Ground
        criteria    <<EOF
    [perl]
        return 'weight' if $Values->{country} eq 'US';
        return 'weight' if ! $Values->{country};
        # Return blank, don't want UPS
        return '';
    [/perl]
    EOF
    
The defineable fields are, in order for the tab-separated format:

=over 4

=item MODE

The unique identifier for that shipping method. It may be repeated
as many times as needed.

=item DESCRIPTION

Text to describe the method (can be accessed on a page
with the C<[shipping-description]> element).

=item CRITERIA

Whether shipping is based on weight, quantity, price, etc. Valid MiniVend
tags can be placed in the field to do a dynamic lookup -- if a number
is returned, that is used as the I<accumulated criteria> -- that is,
the total of weight, quantity, or price as applied to all items in the
shopping cart.

See C<Criteria Determination> below.

=item MINIMUM

The low bound of quantity/weight/criteria this entry applies to.

=item MAXIMUM

The high bound of quantity/weight/criteria this entry applies to.
The first found entry is used in case of ties.

=item COST

The method of developing cost. It can be a number which will
be used directly as the shipping cost, or a function, determined
by a single character at the beginning of the field:

   f       Formula (MML tags OK, evaluated as Perl)
   x       Multiplied by a number
   [uA-Z]  UPS-style lookup
   m       MiniVend chained cost lookup (all items summed together)
   i       MiniVend chained cost lookup (items summed individually)

=item NEXT

The C<next> field supplies an alternative shipping mode to substitute
if the cost of the current one is zero.

=item ZONE

The UPS zone that is being defined.

=item QUERY

MiniVend tags which will return a SQL query to select lines
matching this specification. The current mode is replaced with
this selection. If there is a query parameter of ?, it will be
replaced with the mode name.

=item QUAL 

The geographic qualification (if any) for this mode.

=item PERL

Perl code that is read and determines the B<criterion>, not the cost.
Use the C<cost> option with "f " as the prelim to supply Perl code to
determine cost.

=item TOTAL

Set to the accumulated criterion before passing to Perl.

=item OPT 

Used to maintain UPS and freeform options. Normally these are set
by separate lines in the shipping definition.

=back

=head2 Criteria determination

The criteria field varies according to whether it is the first field in
the shipping file exactly matching the mode identifier.  In that case, it
is called the B<main criterion>.  If it is in subsidiary shipping lines
matching the mode (with optional appended digits) then it is called a
B<qualifying criterion>.  The difference is that the main criterion returns
the basis for the calculation (i.e. weight or quantity) while the qualifying
criterion determines whether the individual line may match the conditions.

The return must be one of:

=over 4

=item quantity

quantity -- The literal value B<quantity> as the main criterion will
simply count the number of items in the shopping cart and return it as
the I<accumulated criteria>. If you want to use a database table field
named C<quantity> then use the C<table::field> notation.

=item o <field name> or <table>::<field name>

A valid database field (column) name as main criterion will
cause the number of items in the shopping cart to be multiplied by the
I<value of the field for each item> to obtain the I<accumulated criteria>.
If the table is not supplied, defaults to the first C<ProductFiles> table.

=item o n.nn

where B<n.nn> is any number.  It will be directly used as the
I<accumulated criteria>.  This can be effectively returned from a
Perl subroutine or MiniVend C<[calc][item-list] ... [/item-list][/calc]>
to create custom shipping routines.

=back

B<IMPORTANT NOTE:> The above only applies to the B<first> field that
matches the shipping mode exactly.  Following B<criteria> fields contain
qualifier matching strings.

=head2 Shipping Calculation modes

There are 8 ways that shipping cost may be calculated. The method used
depends on the I<first character> of the C<cost> field in the shipping 
database.

=over 4

=item N.NN (digits)

If the first character is a digit, then a number is assumed and read 
directly as the shipping cost.

=item e

If the first character is an C<e>, a cost of zero is returned and an
error message is placed in the session value C<ship_message>
(i.e. C<[data session ship_message]> or $Session->{ship_message}).

=item f

If the character C<f> is the first, MiniVend will first interpret the
text for any MiniVend tags and then interpret the result as a formula.
It is really as Perl code; so you may reference the entire set of MiniVend
objects with the code.

=item i

Specifies a chained shipping lookup which will be applied to each item
in the shopping cart.

=item m

Specifies a chained shipping lookup which will be applied to the entire
shopping cart.

=item u

Calls the UPS-style lookup. You can pre-define as many as you wish,
though if you want to do the hundreds available it is best done on
the fly.

=item x

If an C<x> is first, a number is expected and is applied as a fixed
multiplier for the accumulated criterion (@@TOTAL@@).

=item A-Z

If the first character is a capital letter, calls one of the 26 secondary
UPS-style lookup zones. (Deprecated now that zones can be named directly).

=back

=head2 How shipping is calculated

=over 4

=item 1

The base code is selected by reading the value of mv_shipmode
in the user session. If it has not been explicitly set, either
by means of the I<DefaultShipping> directive or by setting the
variable on a form (or in an order profile), it will be C<default>.

The mv_shipmode must be in the character class [A-Za-z0-9_]. If
there are spaces, commas, or nulls in the value they will be read
as multiple shipping modes.

=item 2 

The modes are selected from the d
=item 1

The criterion field is found -- if it is quantity, then it is the total
quantity of items on the order form. If it is any other name, then the
criterion is calculated by multiplying the return value from the product
database field for each item in the shopping cart, multiplied by its quantity.
(If the lookup fails due to the column or row not existing, a zero cost will be
returned, and an error is sent to the catalog error log.) If a number is
returned from a MiniVend tag, then that number is used directly.

=item 1

Entries in the shipping database that begin with the same
string as the shipping mode are examined. If none is found,
a zero cost is returned and an error is sent to the catalog error log.

B<NOTE:> You may use the same mode name for all lines in the same group,
but the I<first> one will contain the main criteria.

=item 4

The value of the accumulated criteria is examined, and if it falls with in the
minimum and maximum, the cost is then applied.

=item 5

If the cost is fixed, it is simply added.

=item 6

If the cost field begins with an C<x>, the cost is multiplied
by the accumulated criterion -- price, weight, etc.

=item 7

If the cost field begins with C<f>, the formula following
is applied. Use @@TOTAL@@ as the value of the accumulated criterion.

=item 8

If the cost field begins with C<u> or a single letter from A-Z,
a UPS-style lookup is done.

=item 9

If the cost field begins with C<s>, a Perl subroutine call is made.

=item 9

If the cost field begins with C<e>, zero cost is returned and an 
error placed in the session B<ship_message> field, available as
C<[data session ship_message]>. 

=back

Here is an example shipping file using all of the methods of
determining shipping cost.

NOTE: The columns are lined up for your reading convenience, the actual
entries should have ONE tab between fields.

 global Option   n/a                 0   0      g PriceDivide
 
 rpsg   RPS      quantity            0   0      R RPS products/rps.csv
 rpsg   RPS      quantity            0   5      7.00
 rpsg   RPS      quantity            6   10     10.00
 rpsg   RPS      quantity            11  150    x .95
  
 usps   US Post  price               0   0      0
 usps   US Post  price               0   50     f 7 + (1 * @@TOTAL@@ / 10)
 usps   US Post  price               50  100    f 12 + (.90 * @@TOTAL@@ / 10)
 usps   US Post  price               100 99999  f @@TOTAL@@ * .05
 
 upsg   UPS      weight [value state]  0   0      e Nothing to ship.
 upsg   UPS      AK HI               0   150    u upsg [default zip 980] 12.00 round
 upsg   UPS                          0   150    u Ground [default zip 980] 2.00 round
 upsg   UPS                          150 9999   e @@TOTAL@@ lb too heavy for UPS

 upsca  UPS/CA   weight              0   0      c C UPS_Canada products/can.csv
 upsca  UPS/CA   weight              -1   -1    o PriceDivide=0
 upsca  UPS/CA   weight              0   150    C upsca [default zip A7G] 5.00
 upsca  UPS/CA   weight              150 99999  e @@TOTAL@@ lb too heavy for UPS
 
 fedex  FedEx    quantity            1   9999   s fedex_cost ;[value country]

=over 4

=item global

This is a global option setting, called out by the C<g> at the beginning.
PriceDivide tells the shipping routines to multiply all shipping settings
by the PriceDivide factor -- except those explicitly set differently with
the C<o> individual modifier.  This allows currency conversion. (Currently
the only option is PriceDivide.)

=item rpsg

If the user selected RPS, (code rpsg), and the quantity on the order
was 3, the cost of 7.00 from the second I<rpsg> line would be applied. If the
quantity were 7, the next entry from the third I<rpsg> line would be selected, for a
cost of 10.00. If the quantity were 15, the last I<rpsg> would be selected, and
the quantity of 15 multiplied by 0.95, for a total cost of 14.25.

=item usps

The next mode, C<usps>, is a more complicated formula -- using price
as the criteria. If the total price of all items in the shopping cart
(same as C<[subtotal]> without quantity price breaks in place) is from 1
to 50, the cost will be 7.00 plus 10% of the order. If the total is from
50.01 to 100, the cost will be 12.00 plus 9% of the order total. If the
cost is 100.01 or greater, then 5% of the order total will be used as
the shipping cost.

=item upsg

The next, C<upsg>, is a special case. It specifies a UPS lookup based on your
UPS zone and two required values (and two optional arguments):

    1. Weight (careful, always use weight for this one!)
    2. The zip/postal code of the recipient, of which only
       the first three digits are used.
    3. A fixed amount to add to the cost found in the UPS
       tables (use 0 as a placeholder if specifying roundup)
    4. If set to 'round', will round the cost up to the next
       integer monetary unit.

If the cost returned is zero, the reason will be placed as an error
message in the session variable ship_message (available as
C<[data session ship_message]>).

UPS weights are always rounded up if any fraction is present. 

The routines use standard UPS lookup tables. First, the UPS Zone
file must be present. That is a standard UPS document,
I<specific to your area>, that you must obtain from UPS and enter into and make
available to MiniVend in TAB-delimited format. (As of March 1997, you
can use the standard .csv file distributed by UPS on their web site at
www.ups.com.) You specify it with the I<UpsZoneFile> directive -- it is
usually named something like C<NNN.csv>, where NNN is the first three
digits of the originating zip code. If you place it in your products directory,
then the directive would look like:

    UPSZoneFile  products/450.csv

Second, you must obtain the cost tables from UPS (again, you can get them from
www.ups.com) and place them into a MiniVend database. That database, its
identifier specified with the first argument (Ground in the example) of the cost
specification, is consulted to determine the UPS cost for that weight and
rate schedule.

In the example below, you would want a database specification like:

    Database  Ground  Ground.csv  CSV

You can append a simple shipping cost qualification to a UPS lookup. If
any additional parameters are present after the five usual ones used for
UPS lookup, they will be interpreted as a Perl subroutine call. The
syntax is the same as if it was encased in the tag C<[perl sub] [/perl]>,
but the following substitutions are made prior to the call:

    @@COST@@  is replaced with whatever the UPS lookup returned
    @@GEO@@   is replaced with the zip (or other geo code)
    @@ADDER@@ is replaced with the defined adder
    @@TYPE@@  is replaced with the UPS shipping type
    @@TOTAL@@ is replaced with the total weight

The example above also illustrates geographic qualification.  If the value of
the form variable B<state> on the checkout form is AK or HI, the U.S. states
Alaska and Hawaii, a $10.00 additional charge (over and above the normal 2.00
handling charge) is made.  This can also be used to select on country, product
type, or any other qualification that can be encoded in the file.

=item upsca

The next entry is just like the UPS definition except it defines a
different lookup zone file (C<products/can.csv>) and uses a different
database, C<upsca>. It also disables the global PriceDivide option for
itself only, not allowing currency conversion. Otherwise, the process
is the same.

You can define up to 27 different lookup zones in the same fashion. If one
of the cost lines (the last field) in the C<shipping.asc> file begins with a
"c", it configures another lookup zone, which must be lettered from C<A> to C<Z>.
It takes the format:

    c X name file* length* multiplier*

where X is the letter from C<A-Z>. The name is used internally as an
identifier and must be present.  The optional C<file> is relative to
the catalog root (like I<UpsZoneFile> is) -- if it is not present the
file equal to C<name> in the products directory (I<ProductDir>) will be
used as the zone file. If the optional digit C<length> is present, that
determines the number of signficant digits in the passed postal/geo code.
When the optional C<multiplier> is present, the weight is multiplied by
it before doing the table lookup. This allows shipping weights in pounds
or kilograms to be adapted to a table using the opposite as the key. Remember,
the match on weight must be exact, and MiniVend rounds the weight up to the
next even unit.

To define the exact equivalent of the UPS lookup zone, you would do:

    c U UPS products/450.csv 3 1

The only difference is that the beginning code to call the lookup is upper-case
C<U> instead of lower-case C<u>.

=item fedex

The last entry, C<fedex>, uses a named subroutine. The example is
designed to work with this subroutine defined in catalog.cfg:

    Sub <<EOF
    sub fedex_cost {
        my($country) = @_;
        my $cost;
        if($country =~ /^usa?$/i) {
            $cost = 20;
        }
        else {
            $cost = 50;
        }
        return $cost;
    }
    EOF

B<NOTE:> The text above appears indented, but in the F<catalog.cfg> file
it must begin at the beginning of the line. Also, make sure you upload
in ASCII mode -- carriage returns are not tolerated.

It will simply return a cost of 20 if the country the user has entered is
US or USA -- and return 50 otherwise. Obviously much more complicated
routines can be defined. Read the following only if you know Perl well
and/or are not of faint heart.

You can call named subroutines with any of the methods, defined with
C<[set name] your_perl_code_here [/set]>, I<Sub>, or I<GlobalSub>.

If parameters are specified, separated by commas, they will be taken as
either fixed values or as database fields to be sent to the subroutine
in an anonymous hash keyed on the item code (for each item in the
*current* shopping cart).

If a database other than the products database is to be used, the database
name should be prepended with a colon (C<:>) separator. If a key other
than the item code is to be used, it should be appended with a semi-colon
separator.

To send fixed value to the subroutine (appended to the call reference
as an array of fixed scalar parameters), begin the parameter with a
semicolon. B<They will be appended globally after the hash reference>.

Examples

  # Sends the weight of each item from the products database
  
  weight 
  
  
  # Sends the value of the handling field from the
  # special database for each item
  
  special:handling 
  
   
  # Sends the value of the 'adder' field from the special
  # database, for the value the user has entered for 'country'
  # The spaces around the separators are OK
 
  special : adder ; [value country] 
  
 
  # Sends a fixed parameter of 20 to the subroutine
  
  ;20

The parameters are interpreted for MiniVend tags before being parsed.
Here is a complete example:

   s item_cost weight,modes:[value mv_shipmode];[value country], ;20, ;25
  
  items in the shopping cart:  00-0011 19=202
  
  ------- product database  ----
  
  code    weight   description      price
  00-0011   8      The Mona Lisa    1000
  19-202    12     American Gothic  800
  
  
  ------- modes database  ----
  
  code    upsg  upsb  upsr  postal_air postal_surface
  UK        0    0     0       1            1 

will call the subroutine C<item_cost>, and will send the weight of each
item, along with the value of the modes database column corresponding to
the shipping mode the user has selected, keyed with the value of C<country>
on their order form. If the user has selected mode postal_air, and is
in the country coded as C<UK>, the subroutine will be called as if it
was:

      item_cost( {
                    '00-0011' => {postal_air => '1', weight => '8'}, 
                    '19-202' => {postal_air => '1', weight => '12'},
                 }, 20, 25 )

If the undefined value is returned by the routine, the next shipping mode
will be tried. If a non-numeric string value is returned, its value
will be placed as an error message in the session variable ship_message
(available as C<[data session ship_message]>) and a zero cost will be returned.
If any number or the empty string is returned, it will be used as the
shipping cost (even 0).

=back

=head2 More on UPS-style lookup

The UPS-style lookup uses two files for its purposes, both of which
need to be in a format like UPS distributes for US shippers.

The I<zone file> is a file that is usually specific to the originating
location. For US shippers shipping to US locations, it is named for the
first three digits of the originating zip code with a CSV extenstion --
for example, C<450.csv>.

It has a format similar to:

    low - high, zone,zone,zone,zone

The C<low> entry is the low bound of the geographic location -- C<high>
is the high bound. (By I<geographic location> we mean zip code.) If the
first digits of the zip code, compared alphanumerically, fall between
the low and high values, that zone is used as the column name for a lookup
in the I<rate database>. The weight is used as the row key.

The first operative row of the zone file (one without leading quotes)
is used to determine the zone column name. In the US, it looks something
like 

Dest. ZIP,Ground,3 Day Select,2nd Day Air,2nd Day Air A.M.,Next Day Air Saver,Next Day Air

MiniVend strips all non-alpha characters and comes up with:

DestZIP,Ground,3DaySelect,2ndDayAir,2ndDayAirAM,NextDayAirSaver,NextDayAir

Therefore, the zone column (shipping type) that you would use use for
UPS ground would be "Ground", and that is what the database should be
named. To support the above, you would want a C<shipping.asc> line
that reads:

    upsg  UPS Ground  weight  0  150  u Ground [default zip 983]

and a C<catalog.cfg> database callout of:

    Database  Ground  Ground.csv  CSV

You can change these column names as long as they correspond
to the C<identifier> of your rate database.

The I<rate database> is a standard MiniVend database. For US shippers,
UPS distributes their rates in a fairly standard comma-separated value
format, with weight being the first (or key) column and the remainder
of the columns corresponding to the zone -- which you obtain from the 
lookup in the zone file.

To adapt other shipper zone files to MiniVend's lookup, you will need
to make it fit the UPS US format. (Most of the UPS international files don't
follow the US format). For example, the 1998 Ohio-US to Canada file begins:

    Canada Standard Zone Charts from Ohio
    Locate the zone by cross-refrencing the first three
    characters of the destination Postal Code in the Postal
    Range column.
 
    Postal Range  Zone
    A0A  A9Z      54
    B0A  B9Z      54
    C0A  C9Z      54
    E0A  E9Z      54
    G0A  G0A      51
    G0B  G0L      54
    G0M  G0S      51
    G0T  G0W      54

You need to change it to

 
    Destination,canstnd
    A0A-A9Z, 54
    B0A-B9Z, 54
    C0A-C9Z, 54
    E0A-E9Z, 54
    G0A-G0A, 51
    G0B-G0L, 54
    G0M-G0S, 51
    G0T-G0W, 54

and match it with a C<canstnd> CSV database that looks like

    Weight,51,52,53,54,55,56
    1,7.00,7.05,7.10,11.40,11.45,11.50
    2,7.55,7.65,7.75,11.95,12.05,12.10
    3,8.10,8.15,8.40,12.60,12.70,12.85
    4,8.65,8.70,9.00,13.20,13.30,13.55
    5,9.20,9.25,9.75,13.85,13.85,14.20
    6,9.70,9.85,10.35,14.45,14.50,14.90
    7,10.25,10.40,11.10,15.15,15.15,15.70
    8,10.80,10.95,11.70,15.70,15.75,16.35
    9,11.35,11.55,12.30,16.40,16.45,17.20

and is called out in catalog.cfg with:

    Database canstnd canstnd.csv CSV

With the above, a 4-pound shipment to postal code E5C 4TL would yield
a cost of 13.20.

=head2  Geographic qualification

If the return value in the main criterion includes whitespace, the
remaining information in the field is used as a qualifier for the
subsidiary shipping modes. This can be used to create geographic
qualifications for shipping, as in:

 upsg   UPS Ground   weight [value state]   0     0     e No items selected
 upsg   UPS Ground   AK HI                  0     150   u Ground [value zip] 12.00
 upsg   UPS Ground                          0     150   u Ground [value zip] 3.00

If C<upsg> is the mode selected, the value of the user session variable
C<state> is examined to see if it matches the geographic qualification on
a whole-word boundary. If it is C<AK> or C<HI>, then UPS Ground with an
adder of 12 will be selected; if it "falls through", then UPS Ground with
an adder of 3 will be selected.

=head2  Handling charges

Additional handling charges can be defined in the shipping file by setting
the form variable C<mv_handling> to a space, comma, or null-separated set
of valid shipping modes.  The lookup and charges are done in the same fashion,
and the additional charges are added to the order.  (The user is responsible for
displaying the charge on the order report or receipt with a C<[shipping handling]>
tag or the like.)  All of the shipping modes found in mv_handling will be
applied -- if multiple instances are found on a form, the accordingly null-separated 
values will all be applied.  Careful! This should not be done in an item-list
unless you take care to account for multiple setting of the variables.

If you wished only to process a handling charge once, you could do safely:

    [item-list]
    [if-item-field very_heavy]
    [perl values]
        return '' if $Values->{mv_handling} =~ /very_heavy/;
        return "<INPUT TYPE=hidden NAME=mv_handling VALUE=very_heavy>";
    [/perl]
    [/if-item-field]
    [/item-list]

A non-blank/non-zero value in the database field B<very_heavy> will then
trigger Perl code which will only set mv_handling once.

=head2 Default Shipping Mode

If a default shipping mode other than C<default> is desired, enter
it into the I<DefaultShipping> directive:

    DefaultShipping     upsg

This will make the  entry on the order form checked by default when the
user starts the order process, if it is put in the form:

  <INPUT TYPE=RADIO NAME=mv_shipmode VALUE=upsg [checked mv_shipmode upsg]> 

To force a choice by the user, you can make mv_shipmode a required form
variable (with I<RequiredFields> or in an order profile) and set F<DefaultShipping>
to zero.

=head1 USER DATABASE

MiniVend has a user database function which allows customers to save any
pertinent values from their session. It also allows the setting of database
or file access control lists for use in controlling access to pages and 
databases on a user-by-user basis.

The database field names in the user database correspond with the form
variable names in the user session. If you have a column named C<address>,
then when the user logs in the contents of that field will be placed
in the form variable C<address>, and will then be availabe for display
with C<[value address]>. Similarly, the database value is available with
C<[data table=userdb column=address key=username]>.

The ASCII file for the database will not reflect changes unless you
export the file with C<[tag export userdb][/tag]>. It is not advisable
to edit the ASCII file, as it will overwrite the real data that is in
the DBM table; user logins and changes would be lost. (This would not
happen with SQL, but editing the ASCII file would have no effect.)
You should probably set the I<NoImport> configuration directive 
accordingly.

This section describes the user database in MiniVend 3.12.  Prior to
3.12, the user database was a demonstration add-on module with a global
subroutine interface. Some of the concepts were the same; the module
interface is much the same; and page code based on the previous version
should work if the C<userdb.cfg> file is still there.

The field names to be used are not set in concrete; they may be changed
with options, and fields may be added or subtracted at any time. Most
users will choose to keep the default demo fields for simplicity sake, as
they cover most common needs. As distributed in the demo, the fields are:

    code
    accounts
    acl
    address
    address_book
    b_address
    b_city
    b_country
    b_name
    b_nickname
    b_phone
    b_state
    b_zip
    carts
    city
    country
    db_acl
    email
    email_copy
    fax
    fax_order
    file_acl
    mv_credit_card_exp_month
    mv_credit_card_exp_year
    mv_credit_card_info
    mv_credit_card_type
    mv_shipmode
    name
    order_numbers
    p_nickname
    password
    phone_day
    phone_night
    preferences
    s_nickname
    state
    time
    zip

A few of those fields are special in naming, though all can be changed via
an option. A couple of the fields are reserved for Minivend's use.

B<IMPORTANT NOTE:> If you are not running with PGP or other encryption for
your credit card numbers, which should NEVER be done anyway, then it is important
that you remove the mv_credit_card_info field from the database. 

The special database fields are:

    accounts         Storage for billing accounts book
    address_book     Storage for shipping address book
    b_nickname       Nickname of current billing account
    carts            Storage for shopping carts
    p_nickname       Nickname for current preferences
    preferences      Storage for preferences
    s_nickname       Nickname for current shipping address
    db_acl           Storage for database access control lists
    file_acl         Storage for file access control lists
    acl              Storage for simple integrated access control

If not defined, the corresponding capability is not available.

NOTE: The fields C<accounts>, C<address_book>, C<carts>, and C<preferences> should
be defined as a BLOB type if you are using SQL. This is also suggested for the acl
fields if those lists could be large.

Reserved fields include:

    code        The username (key for the database)
    password    Password storage
    time        Last time of login

=head2 The [userdb ...] tag

MiniVend provides a C<[userdb ...]> tag to access the UserDB functions.

 [userdb
        function=function_name
        username="username"*
        assign_username=1
        username_mask=REGEX*
        password="password"*
        verify="password"*
        oldpass="old password"*
        crypt="1|0"*
        shipping="fields for shipping save"
        billing="fields for billing save"
        preferences="fields for preferences save"
        ignore_case="1|0"*
        force_lower=1
        param1=value*
        param2=value*
        ...
        ]

* Optional

It is normally called in an C<mv_click> or C<mv_check> setting, as in:

    [set Login]
    mv_todo=return
    mv_nextpage=welcome
    [userdb function=login]
    [/set]

    <FORM ACTION="[process-target]" METHOD=POST>
    <INPUT TYPE=hidden NAME=mv_click VALUE=Login>
    Username <INPUT NAME=mv_username SIZE=10>
    Password <INPUT NAME=mv_password SIZE=10>
    </FORM>

There are several global parameters that apply to any use of
the C<userdb> functions. Most importantly, by default the database
table is set to be I<userdb>. If you must use another table name,
then you should include a C<database=table> parameter with any
call to C<userdb>. The global parameters (default in parens):

    database     Sets user database table (userdb)
    show         Show the return value of certain functions
                 or the error message, if any (0)
    force_lower  Force possibly upper-case database fields
                 to lower case session variable names (0)
    billing      Set the billing fields (see Accounts)
    shipping     Set the shipping fields (see Address Book)
    preferences  Set the preferences fields (see Preferences)
    bill_field   Set field name for accounts (accounts)
    addr_field   Set field name for address book (address_book)
    pref_field   Set field name for preferences (preferences)
    cart_field   Set field name for cart storage (carts)
    pass_field   Set field name for password (password)
    time_field   Set field for storing last login time (time)
    expire_field Set field for expiration date (expire_date)
    acl          Set field for simple access control storage (acl)
    file_acl     Set field for file access control storage (file_acl)
    db_acl       Set field for database access control storage (db_acl)

By default the system crypt() call will be used to compare the
password. This is best for security, but the passwords in the user
database will not be human readable.

If you don't keep critical information and don't do MiniVend
administration via the C<UserDB> capability, then you may
wish to use the <UserDB> directive (described below) to set
encryption off by default:

    UserDB   default   crypt   0

That will set encryption off by default. You can still set encryption
on by passing C<crypt=1> with any call to a C<new_account>, C<change_pass>,
or C<login> call.

=head2 Setting defaults with the UserDB directive

The I<UserDB> directive provides a way to set defaults for
the user database. For example, if you always wanted to save
and recall the scratch variable C<tickets> in the user database
instead of the form variable C<tickets>, you could set:

    UserDB   default   scratch  tickets

That makes every call to C<[userdb function=login]> be equivalent
to C<[userdb function=login scratch=tickets]>.

If you wish to override that default for one call only, you can
use C<[userdb function=login scratch="passes"]>.

If you wish to log failed access authorizations, set the C<UserDB>
profile parameter C<log_failed> true:

    UserDB  default  log_failed 1

To disable logging of failed access authorizations (the default), set
the C<UserDB> profile parameter C<log_failed> to 0:

    UserDB  default  log_failed 0

The I<UserDB> directive uses the same key-value pair settings
as the I<Locale> and I<Route> directives, and you may have more
than one set of defaults. You can set them in a hash structure:

    UserDB  crypt_case  <<EOF
    {
        'scratch'        => 'tickets',
        'crypt'          => '1',
        'ignore_case'    => '0',
    }
    EOF

    UserDB  default  <<EOF
    {
        'scratch'        => 'tickets',
        'crypt'          => '1',
        'ignore_case'    => '1',
    }
    EOF

NOTE: The usual here-document caveats apply -- the EOF must be on
a line by itself with no leading/trailing whitespace.

The last one to be set becomes the default.

The option C<profile> selects the set to use. So if you wanted
usernames and passwords to be case sensitive with no encryption,
you could pass this call:

    [userdb function=new_account profile=case_crypt]

The username and password will be stored as typed in, and the
password will be encrypted in the database.


=head2 User Database functions

The user database features are implemented as a series of functions
attached to the C<userdb> tag. The functions are:

=over 4

=item login

Active parameters: username, password, crypt, pass_field, ignore_case

Log in to Minivend. By default, the username is contained in the
form variable C<mv_username> and the password in C<mv_password>.
If the login is successful, the session value C<username>
(C<[data session username]>) will be set to the user name.

This will recall the values of all non-special fields in the user
database and place them in their corresponding user form variables.

The I<CookieLogin> directive (catalog.cfg) allows users to save
their username/password in a cookie. Expiration time is set by
I<SaveExpire>, renewed every time they log in. To cause the cookie to
be generated originally, the form variable C<mv_cookie_password> or
C<mv_cookie_username> must be set in the login form. The former causes
both username and password to be saved, the latter just the username.

=item logout

Log out of Minivend. No additional parameters are needed.

=item new_account

Active parameters: username, password, verify, assign_username,
username_mask, ignore_case

Create a new account. It requires the C<username>, C<password>, and
C<verify> parameters, which are by default contained in the form
variables C<mv_username>, C<mv_password>, C<mv_verify> respectively.

If you set the C<assign_username> parameter, then UserDB will assign
a sequential username. The C<counter> parameter can be used to set
the filename (must be absolute), or you can accept the default of
CATALOG_DIR/etc/username.counter. The first username will be "U0001"
if the counter doesn't exist already.

The C<ignore_case> parameter forces the username and password to
lower case in the database, in effect rendering the username and
password case-insensitive.

If you set C<username_mask> to a valid Perl regular expression (without
the surrounding / /) then any username containing a matching string will
not be allowed for use. For example, to screen out order numbers from
being used by a random user:

    [userdb function=new_account
            username_mask="^[A-Z]*[0-9]"
            ]

The I<CookieLogin> directive (catalog.cfg) allows users to save
their username/password in a cookie. Expiration time is set by
I<SaveExpire>, renewed every time they log in. To cause the cookie to
be generated originally, the form variable C<mv_cookie_password> or
C<mv_cookie_username> must be set in the login form. The former causes
both username and password to be saved, the latter just the username.

If you want to automatically create an account for every order,
you can do in the I<OrderReport> file:

    [userdb function=new_account
            username="[value mv_order_number]"
            password="[value zip]"
            verify="[value zip]"
            database="orders"
            ]

This would be coupled with a login form that asked for order number and
zip code; thereupon allowing you to display the contents of a transaction
database with (presumably updated) order status information or a shipping
company tracking number.

=item change_pass

Active parameters: username, password, verify, oldpass

Change the password on the currently logged-in account. It requires
the C<username>, C<password>, C<verify>, and C<oldpass> parameters,
which are by default contained in the form variables C<mv_username>,
C<mv_password>, C<mv_verify>, C<mv_password_old> respectively.

=item set_shipping

Active parameters: nickname, shipping, ship_field

Place an entry in the shipping Address book. Example:

    [userdb function=set_shipping nickname=Dad]

See I<Address Book> below.

=item get_shipping

Active parameters: nickname, shipping, ship_field

Recall an entry from the shipping Address book. Example:

    [userdb function=get_shipping nickname=Dad]

See I<Address Book> below.

=item get_shipping_names

Active parameters: ship_field

Gets the names of shipping address book entries and places
them in the variable C<address_book>. By default, it does not return
the values; if you wish them to be returned you can set
the parameter C<show> to 1, as in:

    [set name=shipping_nicknames
         interpolate=1]
      [userdb function=get_shipping_names show=1]
    [/set]

=item set_billing

Active parameters: nickname, billing, bill_field

Place an entry in the billing accounts book. Example:

    [userdb function=set_billing nickname=discover]

See I<Accounts Book> below.

=item get_billing

Active parameters: nickname, billing, bill_field

Recall an entry from the billing accounts book. Example:

    [userdb function=get_billing nickname=visa]

See I<Accounts Book> below.

=item save

Saves all non-special form values that have columns in the user database.

=item set_cart

Save the contents of a shopping cart.

    [userdb function=set_cart nickname=christmas]

See I<Carts> below.

=item get_cart

Active parameters: nickname, carts_field, target

Recall a saved shopping cart. 

    [userdb function=get_cart nickname=mom_birthday]

Setting C<target> saves to a different shopping cart than the
default main cart. The C<carts_field> controls the database
field used for storage.

=item set_acl

Active parameters: location, acl_field, delete

Set a simple acl. Example:

    [userdb function=set_acl location=cartcfg/editcart]

This allows the current user to access the page "cartcfg/editcart" if 
it is access-protected.

To delete access, do:

    [userdb function=set_acl location=cartcfg/editcart delete=1]

To display the setting at the same time as setting use the
C<show> attribute:

    [userdb function=set_acl location=cartcf/editcart show=1]

=item check_acl

Active parameters: location, acl_field

Checks the simple access control listing for a location, returning
1 if allowed and the empty string if not allowed.

    [if type=explicit
        compare="[userdb
                    function=check_acl
                    location=cartcfg/editcart]"
    ]
    [page cartcfg/editcart]Edit your cart configuration[/page]
    [/if]

=item set_file_acl, set_db_acl

Active parameters: location, mode, db_acl_field, file_acl_field, delete

Sets a complex access control value. Takes the form:

    [userdb function=set_file_acl
            mode=rw
            location=products/inventory.txt]

where mode is any value you wish to check for with check_file_acl. As
with the simple ACL, you can use delete=1 to delete the location entirely.

=item check_file_acl, check_db_acl

Active parameters: location, mode, db_acl_field, file_acl_field

Checks a complex access control value and returns a true/false (1/0)
value. Takes the form:

    [userdb function=check_db_acl
            mode=w
            location=inventory]

where mode is any value you wish to check for with check_file_acl. It
will return true if the mode string is contained within the entry
for that location. Example:

    [if type=explicit
        compare="[userdb
                    function=check_db_acl
                    mode=w
                    location=inventory]"
    ]
    [userdb function=set_acl location=cartcfg/edit_inventory]
    [page cartcfg/edit_inventory]You may edit the inventory database[/page]
    [else]
    [userdb function=set_acl location=cartcfg/edit_inventory delete=1]
    Sorry, you can't edit inventory.
    [/if]

=back

=head2 Address Book

I<address_book> is a shipping address book.  The shipping address book
saves information relevant to shipping the order. In its simplest form,
this can be the only address book needed.  By default these form values
are included:

   s_nickname
   name
   fname
   lname
   address
   address1
   address2
   address3
   city
   state
   zip
   country
   phone_day
   mv_shipmode

The first field is always the name of the form variable that contains the
key for the entry.

The values are saved with the C<[userdb function=set_shipping]> tag call, and
are recalled with C<[userdb function=get_shipping]>.  A list of the keys
available is kept in the form value C<address_book>, suitable for iteration in
an HTML select box or in a set of links.

You can get the names of a the addresses with the get_shipping_names function:

    [userdb function=get_shipping_names]

By default, they are placed in the variable C<address_book>. Here is a
little snippet that builds a select box:

    <FORM ACTION="[process-target]" METHOD=POST>
    [userdb function=get_shipping_names]
    [if value address_book]
    <SELECT NAME="s_nickname">
    [loop arg="[value address_book]"] <OPTION> [loop-code] [/loop]
    </SELECT>
    <INPUT TYPE=submit NAME=mv_check VALUE="Recall Shipping">
    </FORM>

The same principle works with accounts , carts, and preferences.

To restore a cart based on the above, you can place in an C<mv_check>
routine:

    [set Recall Shipping]
    mv_todo=return
    mv_nextpage=ord/basket
    [userdb function=get_shipping nickname="[value s_nickname]"]
    [/set]

When the mv_check variable is encountered, the contents of the
scratch variable C<Recall Shipping> are processed and the shipping
address information inserted into the user form values. This is destructive
of any current values of those user session variables, of course.

If you want to change the fields that are recalled or saved, you can
use the C<shipping> parameter:

    [userdb function=get_shipping
            nickname=city_and_state
            shipping="city state"]

Only the values of the C<city> and C<state> variables will be replaced.

=head2 Accounts Book

The accounts book saves information relevant to billing
the order. By default these form values are included:

   b_nickname
   b_name
   b_fname
   b_lname
   b_address
   b_address1
   b_address2
   b_address3
   b_city
   b_state
   b_zip
   b_country
   b_phone
   purchase_order
   mv_credit_card_type
   mv_credit_card_exp_month
   mv_credit_card_exp_year
   mv_credit_card_info

The values are saved with the C<[userdb function=set_billing]> tag call, and
are recalled with C<[userdb function=get_billing]>.  A list of the keys
available is kept in the form value C<accounts>, suitable for iteration in an
HTML select box or in a set of links.

=head2 Preferences

Preferences are miscellaneous session information. They include by default the
fields:

    email
    fax
    phone_night
    fax_order
    email_copy

The field p_nickname acts as a key to select the preference set. You can change
the values that are included with the C<preferences> parameter:
    
    [userdb function=set_preferences
            preferences="email_copy email fax_order fax"]

or in catalog.cfg:

    UserDB default preferences "mail_list email fax_order music_genre"

=head2 Carts

You may save or recall the contents of shopping carts in much the same fashion. 
See the B<simple> demo application C<ord/basket.html> page for an example.

=head2 Controlling page access with UserDB

You can implement a simple access control scheme with the MiniVend
user database. Controlled pages must reside in a directory which has
a file named C<.access> that is zero bytes in length. (If it is more
than 0 bytes, then only the I<RemoteUser> or I<MasterHost> may access
files in that directory.)

Set the following variables in C<catalog.cfg>:

    Variable   MV_USERDB_ACL_TABLE  userdb
    Variable   MV_USERDB_ACL_COLUMN acl

The C<MV_USERDB_ACL_TABLE> is the table which controls access,
and likewise the C<MV_USERDB_ACL_TABLE> names the column in
that database which will be checked for authorization.

The database entry should contain the complete MiniVend-style
page name of the page to be allowed. It will not match substrings.

For example, if the user C<flycat> followed this link:

    <A HREF="[area cartcfg/master_edit]">Edit</A>

Access would be allowed if the contents of the userdb were:

    code    acl
    flycat  cartcfg/master_edit

and disallowed if it were:

    code    acl
    flycat  cartcfg/master_editor

You can enable access with:

    [userdb function=set_acl location="cartcfg/master_edit"]

and disallow access with:

    [userdb function=set_acl
            delete=1
            location="cartcfg/master_edit"]

Of course a pre-existing database with the ACL values will work
as well; it need not be in the UserDB setup.

=head1 TRACKING AND BACKEND ORDER ENTRY

MiniVend allows the entry of orders into a system via one of several
methods. The I<AsciiBackend> capability allows submittal of
parameters to an external order entry script. Support for SQL
allows entry of orders directly into an SQL database. Orders
can be written to an ASCII file. They can be formatted precisely
for email-based systems. The orders can be placed in a DBM file.
Finally, embedded Perl allows completely flexible order entry, 
including real-time credit-card verification and settlement.

=head2 ASCII Backup Order Tracking

If you set AsciiTrack to a legal file name (based in VendRoot unless
it has a leading "/"), a copy of the order will be saved there as well
as being emailed.

If the file name string begins with a pipe "|", a program will be run
and the output "piped" to that program. This allows easy backend entry
of orders with an external program.

=head2 Database Tracking

Once the order report is processed, you can be sure the order will
complete. Therefore it is the perfect place to put MiniVend tags that
make order entries in database tables.

A good model is to place a single record in a database summarizing the
order, and a series of lines that correspond to each line item in the
order. This can be in the same database table; if the order number itself
is the key for the summary, you can append a line number to the order number
to show each line of the order.

The following would summarize a sample order number S00001 for part
number 00-0011 and 99-102:

    code     order_number part_number  quantity   price    shipping  tax
    S00001   S00001                    3          2010     12.72     100.50
    S00001-1 S00001       00-0011      2          1000     UPS       yes
    S00001-2 S00001       99-102       1          10       UPS       yes

You can add fields as appropriate, perhaps with order status, shipping
tracking number, address, customer number, or other information.

The above can be easily done with MiniVend's C<[import ....]> tag
using the convenient C<NOTES> format:

    [set import_status]
    [import table=orders type=LINE continue=NOTES]

    code: [value mv_order_number]
    order_number: [value mv_order_number]
    quantity: [nitems]
    price: [subtotal noformat=1]
    shipping: [shipping noformat=1]
    tax: [salestax noformat=1]

    [/import]
  
    [item-list]
    [import table=orders type=LINE continue=NOTES]

    code: [value mv_order_number]-[item-increment]
    order_number: [value mv_order_number]
    quantity: [item-quantity]
    price: [item-price noformat=1]
    shipping: [shipping-description]
    tax: [if-item-field nontaxable]No[else]Yes[/else][/if]

    [/import][/item-list]


=head2 Custom Order Routing

MiniVend can send order emails and perform custom credit card charges
and/or logging for each individual item. The I<Route> directive is
used to control this behavior, along with the C<mv_order_route> item attribute
and C<mv_order_route> form variable.

Routes are established with the Route directive, which is similar
to the Locale directive. Each route is like a locale, in that you
can set key-value pairs. Here is an example setting:

    Route  VEN  pgp_key         0x67798115
    Route  VEN  email           orders@minivend.com
    Route  VEN  reply           service@minivend.com
    Route  VEN  encrypt         1
    Route  VEN  encrypt_program "/usr/bin/pgpe -fat -q -r %s"
    Route  VEN  report          etc/report_mail

This route would be used whenever the value C<VEN> was contained
in the form variable C<mv_order_route>.

The last route that is defined provides the defaults for all other
routes. For example, if C<encrypt_program> is set there then the same
value will be the default for all routes.

The attributes that can be set are:

=over 4

=item attach

Determines whether the order report should be attached to the main order
report email. This is useful if you must print certain items separately
from others, perhaps for FAX to a fulfilment house.

=item counter

The location of a counter file which should be used instead
of C<OrderCounter> for this route. It will generate a different
value for C<mv_order_number> for the route.

=item credit_card

Determines whether credit card encryption should be done for this
order. Either this or C<encrypt> should always be set.

=item cybermode

If this is set, enables I<CyberCash> for the route. You can also
set the variables CYBER_CONFIGFILE, CYBER_SECRET, and all other
normal CYBERCASH variables. For example:

    Route VEN cybermode         mauthonly
    Route VEN CYBER_CONFIGFILE  config/vendor1_cfg
    Route VEN CYBER_VERSION     3.2

=item email

The email address(es) where the order should be sent. Set just
like the I<MailOrderTo> directive, which is also the default.

=item encrypt

Whether the B<entire order> should be encrypted with the B<encrypt_program>.
If C<credit_card> is set, then the credit card will first be encrypted,
then the entire order encrypted.

=item encrypt_program

The encryption program incantaton which should be used. Set identically
to the I<EncryptProgram> directive, except that %s will be replaced with
the C<pgp_key>. Default is C<pgpe -fat -r %s>.

=item errors_to

Sets the C<Errors-To:> email header so that bounced orders will go
to the proper address. Default is the same as I<MailOrderTo>.

=item increment

Whether the order number should be incremented as a result
of this result. Default is not to increment, as the order number
should usually be the same for different routes within the same
customer order.

=item individual_track

A I<directory> where individual order tracking files will be
placed. The file name will correspond to the value of C<mv_order_number>.
This can be useful for batching orders via download.

=item individual_track_ext

The extension that will be added to the file name for C<individual_track>.
Must contain a period (C<.>) if that is desired.

    individual_track_ext     .pgp

=item pgp_cc_key

The PGP key I<selector> that is used to determine which public key is
used for encryption of credit cards only. With PGP 5 and 6, you can see
appropriate values by using the command C<pgpk -l>.

=item pgp_key

The PGP key I<selector> that is used to determine which public key is
used for encryption. If C<pgp_cc_key> is set, that key will be used
for credit card encryption instead of C<pgp_key>. With PGP 5 and 6,
you can see appropriate values by using the command C<pgpk -l>.

=item profile

The custom order profile which should be performed to check
the order. If it fails, then the route will not be performed.
See I<OrderProfile> and I<mv_order_profile>.

=item receipt

The receipt page that should be used for this routing. This only
makes sense if B<supplant> is set for the route.

=item report

The report page that should be used for this routing. 
If C<attach> is defined, then the contents of the report
will be placed in a MIME attachment in the main order report.

=item reply

The C<Reply-To> header that should be set. Default is the same
as C<email>.

If there are only word characters (A-Za-z0-9 and underscore) then it
describes a MiniVend variable name where the address can be found.

=item supplant

Whether this route should supplant the main order report. If set,
the I<AsciiTrack> operation will use this route and the normal
MiniVend order email sequence will not be performed.

=item track

The name of a file which should be used for tracking. If the
C<supplant> attribute is set, then the normal order tracking
will be used as well.

=item track

A number representing the mode to change either C<track> or
C<individual_track> files to.

=back

An individual item routing causes all items labeld with that route
to be placed in a special sub-cart which will be used for the order
report. This means that the C<[item-list] LIST [/item-list]> will only
contain those items, allowing operations to be performed on subsets of
the complete order.

Here is an example of an order routing:

    Route  HARD  pgp_key          0x67798115
    Route  HARD  email            hardgoods@minivend.com
    Route  HARD  reply            service@minivend.com
    Route  HARD  encrypt          1
    Route  HARD  encrypt_program  "/usr/bin/pgpe -fat -q -r %s"
    Route  HARD  report           etc/report_mail
                                 
    Route  SOFT  email            ""
    Route  SOFT  profile          create_download_link
    Route  SOFT  empty            1

    Route  main  cybermode        mauthonly
    Route  main  CYBER_VERSION    3.2
    Route  main  CYBER_CONFIGFILE etc/cybercash.cfg
    Route  main  pgp_key          0x67798115
    Route  main  email            orders@minivend.com
    Route  main  reply            service@minivend.com
    Route  main  encrypt          1
    Route  main  encrypt_program  "/usr/bin/pgpe -fat -q -r %s"
    Route  main  report           etc/report_all
 
To tell MiniVend that order routing is in effect, the variable
mv_order_route is set on the B<final> order submission form:

    <INPUT TYPE="hidden" NAME="mv_order_route" VALUE="main">

To set the order routing for individual items, some method
of determining their status must be made and the C<mv_order_route>
attribute must be set. This could be set at the time of
the item being placed in the basket, or you can have a
database field called C<goods_type> set to the appropriate
value. We will use this Perl routine on the final order form:

 [perl arg=carts interpolate=1]
    my $string = <<'EOF';
 [item-list][item-code]  [item-field goods_type]
 [/item-list]
 EOF
    my @items;
    my %route;
    @items = grep /\S/, split /\n+/, $string;
    for(@items) {
        my ($code, $keycode) = split /\t/, $_;
       $route{$code} = $keycode;
    }
    my $cart = $Carts->{'main'};
    my $item;
    foreach $item ( @{ $Carts->{'main'} } ) {
        $item->{mv_order_route} = $route{$item->{'code'}} || undef;
    }
    return '';
 [/perl]

Now the individual items are labeled with a C<mv_order_route> value
which will cause their inclusion in the appropriate order routing.

Upon submission of the order form, any item labeled C<HARD> will
be accumulated and sent to the email address C<hardgoods@minivend.com>,
where presumably the item will be pulled from inventory and shipped.

Any item labeled C<SOFT> will be passed to the order profile
C<create_download_link>, which will place it in a staging area for
customer download. (This would be supported by a link on the receipt,
perhaps by reading a value set in the profile).

The C<main> order routing will use CyberCash to charge the order,
and will be completely encrypted for emailing.

=head1 MINIVEND SECURITY

=head2 SSL support

MiniVend has several features that enable secure ordering via SSL
(Secure Sockets Layer). Despite their mystique, SSL 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 and the specification of the URL -- https: is used for the
URL protocol specification instead of the usual http: designation.

B<IMPORTANT NOTE:>  MiniVend attempts to perform operations securely,
but no guarantees or warranties of any kind are made! Since MiniVend
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 C<minivend>, C<minivend.cfg>,
and all modules included in minivend. Check them on a regular basis to
ensure they have not been changed.

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

MiniVend incorporates additional security for credit card numbers. The
field B<mv_credit_card_number> will not ever be written to disk.

To enable automated encryption of the credit card information, you need
to define the directive I<CreditCardAuto> to I<yes>. I<EncryptProgram>
also needs to be defined with some value, one which will, with hope,
encrypt the number. 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 I<CreditCardAuto> for more information on how to set the form variables.

=head2 Administrative Pages

With MiniVend's I<GlobalSub> capability, very complex add-on schemes
can be implemented with Perl subroutines. And with the new
writable database, pages that modify the catalog data can be
made. If you mark a page as an I<AdminPage>, only the catalog
administrator may use it. See I<MasterHost>, I<RemoteUser>, and
I<Password>.

In addition, you can create a file in any MiniVend page subdirectory
called .access. If that file is present and non-zero in size,
any pages in that directory are only available to the catalog
administrator.

=head2 Controlling access to certain pages

If the directory containing the page has a file C<.access>
and that file is zero bytes long, then access can be gated
in one of several ways.

=over 4

=item 1

If the file C<.access_gate> is present, it will be read and
scanned for page-based access. The file has the form:

   page: condition
   *: condition

The C<page> is the file name of the file to be controlled (the .html 
extension is optional); the C<condition> is either a literal C<Yes/No>
or MiniVend tags which would produce a C<Yes> or C<No> (1/0 work
just fine, as do true/false).

The entry for C<*> sets the default action if the page name is not
found. If you want pages to be allowed by default, set it to C<1>
or C<Yes>. If you want pages to be denied by default in this directory,
leave blank or set to C<No>.
Here is an example, for the directory C<controlled>, having the
following files:

  -rw-rw-r--   1 mike     mike            0 Jan  8 14:19 .access
  -rw-rw-r--   1 mike     mike          185 Jan  8 16:00 .access_gate
  -rw-rw-r--   1 mike     mike          121 Jan  8 14:59 any.html
  -rw-rw-r--   1 mike     mike          104 Jan  8 14:19 bar.html
  -rw-rw-r--   1 mike     mike          104 Jan  8 14:19 baz.html
  -rw-rw-r--   1 mike     mike          104 Jan  8 14:19 foo.html

The contents of C<.access_gate>:

    foo.html: [if session username eq 'flycat']
                Yes
              [/if]
    bar:      [if session username eq 'flycat']
              [or scratch allow_bar]
                Yes
              [/if]
    baz:      yes
    *:        [data session logged_in]

The page C<controlled/foo> is only allowed for the logged-in user
B<flycat>.

The page C<controlled/bar> is allowed for the logged-in user
B<flycat>, or if the scratch variable C<allow_bar> is set to
a non-blank, non-zero value.

The page C<controlled/baz> is always allowed for display.

The page C<controlled/any> (or any other page in the directory not
named in C<.access_gate>) will be allowed for any user logged
in via I<UserDB>.

NOTE: The C<.access_gate> scheme is available for database access checking
if the database is defined as an I<AdminDatabase>. The C<.access_gate>
file is located in I<ProductDir>.

=item 1

If the I<Variable> C<MV_USERDB_REMOTE_USER> is set (non-zero
and non-blank) then any user logged in via the UserDB feature
will receive access to all pages in the directory.

NOTE: If there is a C<.access_gate> file, it overrides this.

=item 1 

If the variables C<MV_USERDB_ACL_TABLE> is set to a valid database
identifier, the I<UserDB> module can control access with simple ACL
logic. See I<USER DATABASE>.

NOTE: If there is a C<.access_gate> file, it overrides this. Also,
if MV_USERDB_REMOTE_USER is set, then this capability is not available.

=back

=head1 INTERNATIONALIZATION

MiniVend has a rich set of I18N features to allow conditional message
display, differing price formats, different currency definitions, price
factoring, sorting, and other settings.

The definitions are maintained in the catalog.cfg file through the use
of built in POSIX support and MiniVend's I<Locale> directive.

All settings are independent for each catalog and each user of that
catalog -- you can have customers accessing the same catalog in any
of an unlimited number of languages and currencies.

=head2 Setting the locale

The locale could be set to C<fr_FR> (French for France) in one of two ways:

=over 4

=item [setlocale locale=locale* currency=locale* persist=1*]

This tag is for new-style tags only and will not work for C<[old]>.

Immediately sets the locale to C<locale>, and will cause it to persist
in future user pages if the C<persist> is set to a non-zero, non-blank
value. If the C<currency> attribute is set, modifies the pricing and
currency-specific locale keys and MiniVend configuration directives
to that locale. If there are no arguments, sets it back to the user's
default locale as defined in the scratch variables C<mv_locale> and
C<mv_currency>.

This allows:

    Dollar Pricing:

    [setlocale en_US]
    [item-list]
    [item-code]: [item-price]<BR>
    [/item-list]

    Franc Pricing:

    [setlocale fr_FR]
    [item-list]
    [item-code]: [item-price]<BR>
    [/item-list]

    [comment] Return to the user's default locale [/comment]
    [setlocale]

This tag is only available in new mode ("NewTags Yes" or C<[new]>).

=item [page process/locale/fr_FR/page/catalog]

This is the same as C<[page catalog]>, except when the link is followed
it will set the locale to C<fr_FR> before displaying the page. This
is persistent.

=item [page process/locale/fr_FR/currency/en_US/page/catalog]

This is the same as C<[page catalog]>, except when the link is followed it
will set the locale to C<fr_FR> and the pricing/number display to the
locale C<en_US> before displaying the page. This is persistent.

=back

Once the locale is persistently set for a user, it is in effect for the
duration of their session.

=head2 MiniVend Locale Settings

The C<Locale> directive has many possible settings, allowing
complete internationalization of page sets and currencies. The
C<Locale> directive is defined in a series of key/value pairs,
with a key (which contains word characters only) followed by 
a value.  The value should be surrounded with double quotes if
it contains whitespace. In this example, the key is C<Value setting>.

    Locale fr_FR "Value setting" "Configuration de valeur"
    Locale de_DE "Value setting" Werteinstellung

When accessed via the special tag C<[L]Value setting[/L]>,
the value C<Configuration de valeur> will be displayed B<only> if the
locale is set to C<fr_FR>. If the locale is set to C<de_DE>,
the string C<Werteinstellung> will be displayed. If it is neither,
the default value of C<Value setting> will be displayed.

The C<[L]> and C<[/L]> must be capitalized -- this is done for speed of processing
as well as easy differentiation in text.

Another, perhaps better, way to do this is right in the page. The
C<[LC]> ... C<[/LC]> pragma pair allows you to specify locale-dependent text.

  [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]

Or you can place an entire new page in place of the default one if
you define the locale key. When locale is in force, and a key named
HTMLsuffix is set to the locale, MiniVend first looks for a page with
a suffix corresponding to the locale. For example:

    <A HREF="[area index]">Catalog home page</A>

If a page index.html exists, it will be the default. If the current locale
is C<fr_FR>, a page "index.fr_FR" exists, and you have in Locale:

    Locale fr_FR HTMLsuffix  fr_FR

then the C<.fr_FR> page will be used instead of the C<.html> page.

For longer series of strings, the configuration file recognizes:

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

        "Search",
        "Recherche"
    }
    EOF

The above sets two string substitutions.  As long as this is valid Perl
syntax describing a series of settings, the text will be matched. It can
contain any arbitrary set of characters that don't contain C<[L]> and C<[/L]>.
If using double quotes, string literals like \n and \t are recognized.

You can also use a database to set locale information. It is added to
any in the catalog.cfg file, and values in it will overwrite previous
settings. See I<LocaleDatabase>.

The C<[L]default text[/L]> is set before any other page processing. It is
equivalent to the characters "default text" or the appropriate Locale
translation for all intents and purposes. Minivend tags and Variable
values can be embedded.

Since the C<[L] message [/L]> substitution is done before any tag processing,
you cannot do C<[L][item-data table field][/L]> and expect success. There
is an add-on C<[loc] message [/loc]> I<UserTag> supplied with the
distribution beginning at V3.09. It does the same thing as C<[L] [/L]> except
after all tag substitution is done. See F<minivend.cfg.dist> for the
definition.

You will need to be quite careful in editing pages with localization
information. Changing even one character of the message will change the
key value and invalidate the message for other languages. To prevent this,
you can instead use:

    [L key]The default.[/L]

The key C<msg_key> will then be used to index the message.  This may be
preferable for many applications.

A C<localize> script is included with minivend. It will parse files
included on the command line and produce output that can be easily edited
to produce localized information. Given an existing file, it will merge
new information where appropriate.

=head2 Special Locale keys for price representation

MiniVend honors the standard POSIX keys:

    mon_decimal_point decimal_point 
    mon_thousands_sep thousands_sep 
    currency_symbol   int_currency_symbol
    frac_digits       p_cs_precedes

See the POSIX setlocale(3) man page for more information.

These will be used for formatting prices and will approximate the number
format used in most countries. To set the price format to something that
is exactly how you want it, you can use the special keys:

=over 4

=item price_picture

MiniVend will format a currency number based on a "picture" given to it. The
basic form is:

    Locale en_US price_picture "$ ###,###,###.##"

This is the presumably for locale C<en_US>, the United States, and it would
display C<4452.3> as C<$ 4,445.30>.

The same display can be achieved with:

     Locale en_US mon_thousands_sep ,
     Locale en_US mon_decimal_point .
     Locale en_US p_cs_precedes     1
     Locale en_US currency_symbol   $

A common price_picture for European countries would be C<###.###.###,##>,
which would display the same number as C<4.452,30>. To add a franc
notation at the end for the locale C<fr_FR>, you would use the setting:

    Locale fr_FR price_picture "##.###,## fr"

IMPORTANT NOTE: The decimal point in use, set by C<mon_decimal_point>, and
the thousands separator C<mon_thousands_sep> B<must> match the settings in
the picture. The C<frac_digits> setting is not used in this case -- it is
derived from the location of the decimal (if any).

The same setting for fr_FR above can be achieved with:

     Locale fr_FR mon_thousands_sep .
     Locale fr_FR mon_decimal_point ,
     Locale fr_FR p_cs_precedes     0
     Locale fr_FR currency_symbol   fr

If the number of digits is greater than the # locations in the picture,
the digits will be changed to asterisks. An overflow number above would
show as C<**.***,** fr>.

=item picture

Same as C<price_picture>, but sets the value returned if the C<[currency]>
tag is not used.

If the number of digits is greater than the # locations in the picture,
the digits will be changed to asterisks, displaying something like
C<**,***.**>.

=back

=head2 Dynamic locale directive changes

If you set Locale keys corresponding to some MiniVend C<catalog.cfg> directives,
those values will be set when setting the locale.

=over 4

=item PageDir

To use a different page directory for different locales, set the PageDir
key. To have two separate language page sets, French and English, you would
set:

    # Establish the default at startup
    PageDir   english
    Locale fr_FR  PageDir  francais
    Locale en_US  PageDir  english

=item ImageDir

To use a different image directory for different locales, set the ImageDir
key. To have two separate language button sets, French and English, you would
set:

    # Establish the default at startup
    ImageDir   /images/english/
    Locale fr_FR  ImageDir   /images/francais/
    Locale en_US  ImageDir   /images/english/

=item ImageDirSecure

Same as ImageDir.

=item PriceField

To use a different field in the products database for pricing based on locale,
set the PriceField locale setting.

    # Establish the default at startup
    PriceField    price
    Locale fr_FR  PriceField  prix

The default will always be C<price>, but if the locale C<fr_FR> is set, the
PriceField directive will change to C<prix> to give prices in francs instead
of dollars.

If I<PriceBreaks> is enabled, then the field C<prix> from the C<pricing> database
will be used to develop the quantity pricing. NOTE: If no Locale settings are
present, it will always be C<price>, irrespective of the C<PriceField> setting.
Otherwise it will always match C<PriceField>.

=item PriceDivide

Normally used to enable penny pricing with a setting of 100, this can be
used to do an automatic conversion factor based on locale.

    # Default at startup is 1 if not set
    # Franc is strong these days!
    Locale fr_FR  PriceDivide  .20

The price will now be divided by C<.20>, yielding franc prices five times
that of the dollar.

=item PriceCommas

Perhaps this isn't a good name anymore, but it controls whether the
C<mon_thousands_sep> will be used for standard currency formatting.
Ignored if using C<price_picture>. Set to 1 or 0, to enable and disable
respectively. DO NOT USE Yes and No.

    # Default at startup is Yes if not set
    PriceCommas  Yes
    Locale fr_FR  PriceCommas  0
    Locale en_US  PriceCommas  1

=item UseModifier

Changes the fields from that set shopping cart options.

    # Default at startup is 1 if not set
    # Franc is strong these days!
    UseModifier format
    Locale fr_FR  UseModifier formats

If a previous setting was made for an item based on another locale, it
will be maintained.

=item PriceAdjustment

Changes the fields from UseModifier that will be used to adjust pricing.
used to do an automatic conversion factor based on locale.

    # Default at startup
    PriceAdjustment  format
    Locale fr_FR  PriceAdjustment  formats


=item TaxShipping,SalesTax

Same as the standard directives.

=item DescriptionField

This changes the field accessed by default with the C<[item-description]> and
C<[description code]> tags.

    # Establish the default at startup
    DescriptionField    description
    Locale fr_FR  DescriptionField desc_fr

=item The [locale] tag

You can set standard error messages based on Locale settings. Make sure
you don't use any of the predefined keys -- it will be safe if you begin
the key with msg_ or such. The default message is set between the 
C<[locale key]> and C<[/locale]> tags. See the example above.

=back

=head2 Sorting based on Locale

The MiniVend C<[sort database:field]> keys will use the LC_COLLATE
setting for a locale provided that:

=over 4

=item *

Your operating system and C compiler have such support for POSIX, and
you have the locale definitions set.

=item *

The locale setting matches one of your configured locales.

=back

It is beyond the scope of this document to discuss these issues,
and you should contact your system administrator or local wizard
to help you set up locales on your system. NOTE: Windows locales
are not supported for sorting.

If you had this arbitrary database named C<letters>:

    code        letter
    00-0011     f
    99-102      é
    19-202      a

and this loop:

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

Using the default C setting for LC_COLLATE it would display:

    a  19-202
    f  00-0011
    é  99-102

If the proper LC_COLLATE settings for locale C<fr_FR> were in effect,
then it would become:

    a  19-202
    é  99-102
    f  00-0011

=head2 Placing Locale information in a Database

MiniVend has the capability to read its locale information from a database,
named with the I<LocaleDatabase> directive. The database can be of any
valid MiniVend type. The locales are in columns, and the keys are in
rows. So to set up price information:

    key                 en_US   fr_FR   de_DE
    PriceDivide         1       .1590   .58
    mon_decimal_point   .       ,       ,
    mon_thousands_sep   ,       .        
    currency_symbol     $        frs    DM 
    ps_cs_precedes      1       0       0   

That would translate I<exactly> into:

    Locale en_US PriceDivide         1
    Locale en_US mon_decimal_point   .
    Locale en_US mon_thousands_sep   ,
    Locale en_US currency_symbol     $
    Locale en_US ps_cs_precedes      1

    Locale fr_FR PriceDivide         .1590
    Locale fr_FR mon_decimal_point   ,
    Locale fr_FR mon_thousands_sep   .
    Locale fr_FR currency_symbol     " frs"
    Locale fr_FR ps_cs_precedes      0

    Locale de_DE PriceDivide         .58
    Locale de_DE mon_decimal_point   ,
    Locale de_DE mon_thousands_sep   " "
    Locale de_DE currency_symbol     "DM "
    Locale de_DE ps_cs_precedes      1           

These settings add on to B<but overwrite> any that are set in the catalog
configuration files, including any #include files.

IMPORTANT NOTE: The information is only read on catalog configuration. It is
not reasonable to access a database for translation or currency conversion in
the normal course of events.

=head1 MINIVEND CONFIGURATION FILES

MiniVend can and usually does run multiple catalogs on the
same server. If no I<Catalog> directives are present in the main
F<minivend.cfg> file, the server will read only a single catalog's
information from the one F<minivend.cfg> file, just as in
early versions of the program.

You normally call configuration directives with the directive as the
first word on the line, with it's value or values following. Leading
whitespace is stripped from the value.

You may call additional files with a rudimentary I<#include file>
statement. The directives called with includes are always appended
at the end of the main configuration file. Though order is rarely
important in the configuration files, you must define any directory
settings in the main configuration file near the top if they are to
be used to base the file calls of subsequent directives. Files are
relative to the catalog directory (or MiniVend software directory, if
in the main minivend.cfg file).

You can also use a type of "here document" to specify MiniVend directives,
with the usual <<MARKER syntax. No semicolon is used to terminate the
marker.

=head1 MINIVEND.CFG

The VendRoot directory, specified in the main program C<minivend>, is
the default location of all of the MiniVend program, configuration,
special, and library files. Unless changed in C<minivend>, the main
MiniVend server configuration file will be minivend.cfg in the VendRoot
directory.

If no I<Catalog> directives are present, only the special MiniVend
admin catalog will be started.

The directives defined in minivend.cfg affect the entire MiniVend
server and all catalogs running under it; though you may run multiple
MiniVend servers on the same machine with totally independent operation.

=head2 ActionMap

Specifies an action for use as a default. If the same action is specified
in C<catalog.cfg>, it will pertain. See I<ActionMap> in that section.

=head2 AddDirective

Adds a configuration directive that will be parsed for every C<catalog.cfg>
file.

Accepts three parameters -- the name of the directive, the name of the
parser (if any), and the default value (if any). The following definition
would add a directive "Foo", with parser "parse_bar", and a default
value of "Hello, world!":

    AddDirective  Foo  bar "Hello, world!"

If the parser is not defined, the directive value will be scalar and
the same as what the user passes in the config file. If defined, the
parser must be extant before it can be referenced, is always resident in
Vend::Config, and begines with the string C<parse_>; the above example
would use.  Examples can be found in the files in the distribution
software directory C<compat/>.

=head2 AdminSub

Marks a global subroutine for use only by catalogs that are set to C<AllowGlobal>
below. Normally global subroutines can be referenced (in embedded Perl) by any
catalog.

    AdminSub  dangerous

=head2 AllowGlobal

Specifies catalog identifiers that may define subroutines and UserTag
entries that can operate with the full permissions of the
server. B<DON'T USE THIS UNLESS YOU TRUST THE CATALOG USER IMPLICITLY.>
Default is blank.

    AllowGlobal  simple

=head2 Catalog

Specifies a catalog that can run using this MiniVend server.
There are three required parameters, separated by spaces and/or tabs.

The first is the name of the catalog -- it will be referred
to as that name in error, warning, and informational messages.
It must contain only alphanumeric characters, hyphens, and underscores.

The second is the base directory of the catalog. If the directory
does not contain a F<catalog.cfg> file, the server will report
an error and refuse to start.

The third directive is very important to get right -- it is the
SCRIPT_NAME of the vlink program that runs the catalog. It must
be unique from other CGI program paths that run on this server --
that is how the catalog is selected for operation.

    Catalog  simple /home/minivend/simple /cgi-bin/simple /simple

You can specify any number of alias script names as additional
parameters. This allows the calling path to be different while still
calling the same catalog -- it is most probably useful when calling an
SSL server or a members-only executable that requires a username/password
via HTTP Basic authorization. All branched links will be called using
the aliased URL.

Optionally, you can call individual C<Catalog> lines that specify the different
parameters. The equivalent of the above is:

    Catalog simple directory /home/minivend/simple
    Catalog simple script    /cgi-bin/simple
    Catalog simple alias     /simple

You can also specify global directives that will change I<for that catalog only>.
This is mostly useful for C<ErrorFile> and C<DisplayErrors>:

    Catalog simple directive ErrorFile /var/log/mvend/simple_error.log

=head2 CheckHTML

Set to the name of an external program that will check the users
HTML when they set C<[flag checkhtml]> or C<[tag flag checkhtml][/tag]>
in their page.

    CheckHTML  /usr/local/bin/weblint

=head2 DisplayErrors

While all errors are reported in the error log file, you can also have
errors displayed by the browser. This is convenient while you are
testing your configuration. Unless this is set, the DisplayErrors
setting in the user catalogs will have no effect. Default is I<No>.

    DisplayErrors       Yes

NOTE: This changes the value of $SIG{__DIE__} and may have other effects
on program operation. This should B<never> be used for normal operation.

=head2 DomainTail

Implements the domain/IP session qualifiers so that only the
major domain is used to qualify the session ID.  This is a 
compromise on security, but it allows non-cookie-accepting browsers
to use multiple proxy servers in the same domain.
 
Default is yes.

    DomainTail No

If you are encrypting your credit cards with PGP or GPG, or are
using a payment service like CyberCash, you may want to look at
the I<WideOpen> directive, which enables more browser compatibility
at the cost of some security.

=head2 DumpStructure

Tells Minivend to dump the structure of catalogs and the minivend server
to a file with the catalog name and the extension C<.structure>. You can
use this to see what directives are I<really> getting set to.

=head2 Environment

Environment variables to inherit from the calling CGI link program.
And example might be PGPPATH, used to set the directory which PGP
will use to find its key ring.

    Environment  MOD_PERL REMOTE_USER PGPPATH

=head2 ErrorFile

Sets the name of the global error log -- the default is C<error.log> in the
MiniVend software directory.

    ErrorFile  /var/log/mvendlog

Of course the user ID running the MiniVend server must have permission to
write that file.

You can set up syslog(8) error logging as well. See C<SysLog>.

=head2 FormAction

Allows you to set up a form action (like the standard ones
C<return, submit, refresh,> etc.). It requires a Perl subroutine
as a target:

    FormAction foo <<EOR
    sub {
        $CGI->{mv_nextpage} = 'bar';
    }
    EOR

If it returns a true (non-zero, non-empty) value, MiniVend will display the
page defined in $CGI->{mv_nextpage}. Otherwise, MiniVend will not display
any page (assuming you took care of that in your routine).

You can override the default MiniVend actions if you desire.

There is also a catalog-specific version of this directive, which overrides
any action of the same name.

=head2 FullUrl

Normally MiniVend determines which catalog to call by determining the
SCRIPT_NAME from the CGI call. This means that different (and maybe
virtual) hosts cannot use the same SCRIPT_NAME to call different
catalogs. Set FullUrl to B<Yes> to differentiate based on the calling
host.  You must then set the server name in the Catalog directive
accordingly, i.e. yourdomain.com/cgi-bin/simple. A yes/no directive,
the default is B<No>.

    FullUrl  Yes

If you set it in this fashion, you must define C<all> catalogs in this
fashion. NOTE: The individual catalog setting will not work, as this is
used before the catalog name is known.

=head2 GlobalSub

Defines a F<global> subroutine for use by the C<[perl sub] subname arg [/perl]>
construct. Use the "here document" capability of MiniVend configuration
files to make it easy to define:

    GlobalSub <<EOF

    sub count_orders {
        my $counter = new File::CounterFile "/tmp/count_orders", '1';
        my $number = $counter->inc();
        return "There have been $number orders placed.\n";
    }
    EOF

As with Perl "here documents", the EOF (or other end marker) must be the 
ONLY thing on the line, with no leading or trailing white space. Do not
append a semicolon to the marker. (The above appears indented -- it
should not be that way in the file!)

B<IMPORTANT NOTE:> These global subroutines are B<not> subject to
security checks -- they can do most anything!  For most purposes,
scratch subroutines or catalog subroutines (also I<Sub>) are better.

GlobalSub routines are subject to full Perl I<use strict> checking, so
you will get errors if you do not use lexical variables or complete
package qualifications for your variables.

=head2 HammerLock

The number of seconds after which a locked session could be
considered to be lost due to malfunction. This will kill the
lock on the session. Only here for monitoring of session
hand-off, if this error shows up in the error log the system
setup should be examined. Default is 30.

    HammerLock          60

=head2 HouseKeeping

How often, in seconds, the MiniVend server will "wake up"
and look for user reconfiguration requests and hung search
processes. On some systems, this wakeup is the only time the
server will terminate in response to a stop command. Default
is 60.

    HouseKeeping    5

=head2 IpHead

Implements the domain/IP session qualifiers so that only the first
C<IpQuad> dot-quads of the IP address are used to qualify the session ID.
The default is 1. This is a slight compromise on security, but it allows
non-cookie-accepting browsers like AOL's V2.0 to use multiple proxy
servers.

DomainTail is preferable unless one of your HTTP servers does
not do host name lookups.

Default is C<No>, and DomainTail must be set to C<No> for it to
operate.

    IpHead Yes

=head2 IpQuad

The number of dot-quads that IpHead will look at. Default is 1.

    IpQuad  2

=head2 Legacy

Makes a few optimizations for newer catalogs. Set to C<Yes> for the maximum
compatibility with older MiniVend catalogs.

=head2 Locale

Sets the global C<Locale> for use in error messages.

=head2 LockoutCommand

The name of a command (as you would enter it from the shell)
that will lock out the host IP of an offending system. The
IP address will be substituted for the first occurrence of the
string %s.

This will be executed with the user ID that MiniVend runs
under, so any commands that require root access will have
to be wrapped with an SUID program.

On Linux, you might lock out a host with:

    ipfwadm -I -i deny -S %s

This would require root permissions, however, under normal
circumstances.  You can use C<sudo> or another method to
wrap and allow the command.

You can write a script which modifies an appropriate access
control file, such as .htaccess for your CGI directory, to
do another level of lockout.  A simple command line containing
C<perl -0777 -npi -e 's/deny/deny from %s\ndeny/' /home/me/cgi-bin/.htaccess>
would work as well (remember, the %s will become the IP
address of the offending user).

    LockoutCommand   lockout %s

=head2 Mall

Set to C<Yes> to issue cookies only for the current catalog's script. By default,
when MiniVend issues a cookie it does so for the base domain. This will allow
multiple catalogs to operate on the same domain without interfering with each others
session ID.

A yes/no directive.

=head2 MaxServers

The maximum number of servers that will be spawned to handle page
requests. If more than MaxServers requests are pending, they will
be queued (within the defined capability of the operating system,
usually 5 pending requests) until the number of active servers goes
below that value.

    MaxServers     4

Default is 10.

=head2 NoAbsolute

Whether MiniVend C<[file ...]> and other tags can read any file on the system
(that is readable by the user id running the MiniVend daemon).  The default is
B<No>, which allows any file to be read -- this should be changed in a
multi-user environment to minimize security problems.

    NoAbsolute     Yes

=head2 PIDcheck

If non-zero, enables a check of running MiniVend processes during the
housekeeping routine. If a process has been running (or is hung) for
longer than I<PIDcheck> seconds then a kill -9 will be issued and the
server count decremented.

During the housekeeping routine, the number of servers checked by 
I<MaxServers> will be recounted based on PID files.

Default is B<0>, disabling the check. It is recommended that you
use this only for MiniVend 3.12 or higher, and it is recommended that
you B<do> use it for that version.

    PIDcheck   300

If you have long-running database builds, you will need to disable
this, set it to a high value (perhaps 600, for 10 minutes), or use the
I<offline> script.

=head2 PIDfile

The file which will contain the MiniVend server process ID -- so that it
can be read to determine which proces should be sent a signal for stopping
or reconfiguring the server.

    PIDfile  /var/run/mvend/minivend.pid

This file must be writable by the MiniVend server user ID, of course.

=head2 SafeUntrap

Sets the codes that will be untrapped in the Safe.pm module, used
for embedded Perl and conditional operations. You can see the Safe.pm
documentation by typing C<perldoc Safe> at the command prompt. The default
is C<249 148> for Perl 5.003, and C<ftfile sort> for Perl 5.003_20 and
above, which untraps the file existence test operator and the sort
operator. Define it as blank to not allow any besides the default
restrictive operators.

    SafeUntrap     ftfile sort ftewrite

=head2 SendMailProgram

Specifies the program used to send email. Defaults to
'/usr/lib/sendmail'. If it is not found at startup, MiniVend
will complain and refuse to start.

    SendMailProgram     /bin/mailer

A value of 'none' will disable the sending of emailed orders. Orders
must be read from a tracking file, log, or by other means.

=head2 SocketFile

The name of the file which is used for UNIX-domain socket communications.
Must be in a directory where the MiniVend user has write permission.

    SocketFile  /var/run/mvend/minivend.socket

Default is C<etc/socket> or the value of the environment variable
MINIVEND_SOCKET.  If set, it will override the environment. You can
set it on the command line as well:

    bin/minivend -r SocketFile=/tmp/mv.socket

=head2 SocketPerms

The permissions (prepend a 0 to use octal notation) that should be used
for the UNIX-domain socket. You might temporarily set this to 666 on
the command line to debug a permission problem on C<vlink>.

    bin/minivend -r SocketPerms=0666

=head2 SubCatalog

Allows definition of a catalog which shares most of the characteristics of
another catalog.  Only the directives that are changed from the base catalog
are added.  The parameters are 1) the catalog ID 2) the base catalog ID,
3) the directory to use (typically the same as the base catalog), and 4)
the SCRIPT_NAME that will trigger the catalog. Any additional parameters
are aliases for the SCRIPT_NAME.

The main reason that this would be used would be to conserve memory in
a series of stores that share most of the same pages or databases.

    SubCatalog   sample2 sample /usr/catalogs/sample /cgi-bin/sample2

=head2 SysLog

Allows you to set up syslog(8) error logging for MiniVend.

    SysLog  command  /usr/bin/logger
    SysLog  tag      mv1
    SysLog  alert    local3.warn
    SysLog  warn     local3.info
    SysLog  info     local3.info

This would cause global errors to be logged with the command:

    /usr/bin/logger -t mv1 -p local3.alert

and cause system log entries something like:

    Oct 26 17:30:11 bill mv1: Config 'co' at server startup 
    Oct 26 17:30:11 bill mv1: Config 'homefn' at server startup 
    Oct 26 17:30:11 bill mv1: Config 'simple' at server startup 
    Oct 26 17:30:11 bill mv1: Config 'test' at server startup 
    Oct 26 17:30:13 bill mv1: START server (2345) (INET and UNIX) 

Of course you can wrap your own wrapper around it to get it to behave how you want.

=head2 TcpHost

When running in INET mode, using C<tlink>, specifies the hosts that are
allowed to send/receive transactions from any catalog on this MiniVend
server. Can be either an name or IP number, and multiple hosts can be
specified in a space-separated list. Default is localhost.

    TcpHost         localhost secure.domain.com

=head2 TcpMap

When running in INET mode, using C<tlink> or the internal
HTTP server, specifies the port(s) which will be monitored.
by the MiniVend server. Default is 7786>

To use the internal HTTP server (perhaps only for password-protected
queries), you can map a catalog to a port. If you had three catalogs
running on the server I<www.minivend.com>, named C<simple>, C<sample>,
and C<search>, you might have in the directive:

    TcpMap    7786 -  7787 simple 7788 sample 7789 search

NOTE: To map large numbers of ports, you can use the <<MARKER here
document notation in minivend.cfg.

With this in effect, the internal HTTP server would map the following
addresses:

    *:7786   mv_admin
    *:7787   simple
    *:7788   sample
    *:7789   search

NOTE: This does not pertain to the use of C<tlink>, which still
relies on the CGI SCRIPT_PATH.

To enable this, the SCRIPT_PATH aliases /simple, /sample, etc. must
be set in the C<Catalog> directive. This would look like:

  Catalog  simple  /home/minivend/catalogs/simple /cgi-bin/simple /simple

If you want to bind to specific IP addresses, add them in the same
fashion that you would an Apache Listen directive:

	TcpMap <<EOF
		127.0.0.1:7786         -
		www.minivend.com:7787  -
	EOF

NOTE: As usual, the EOF should be at the beginning of a line with no leading
or trailing whitespace.

=head2 TemplateDir

Sets a directory which will be searched for pages if not found in the users
C<pages> directory. I<MiniMate> uses this; you could use it to supply some default
pages so the user will not have them in their directory.

    TemplateDir    /usr/local/minivend/default_pages

The user's page, if it exists, will take precedence.  There is also a
catalog-specific version of this directive; if a page is found in that
directory (or directories) then it will take precedence.

=head2 UserTag

This defines a UserTag which is global in nature, meaning not
limited by the I<Safe.pm> module, and which tag is available to
all MiniVend catalogs running on the server. Otherwise this is
the same as a catalog I<UserTag>.

=head2 Variable

Defines a global variable that will be available in B<all> catalogs
with the notation @@Variable@@. Variable identifiers must begin with a
capital letter, and can contain only word characters (B<A-Z,a-z,0-9> and
underscore) -- they are case-sensitive. If using the
I<ParseVariables> directive, only variables in ALL CAPS will be
parsed. These are substituted first in any MiniVend page, and can
contain any valid MiniVend tags including catalog variables.

    Variable   DOCUMENT_ROOT   /usr/local/etc/httpd/htdocs

There are several standard variables which you should not
use:

=over 4

=item MV_FILE

Name of the last file read in, as in C<[file ...]> or an externally located perl routine

=item MV_NO_CRYPT

Set this to 1 to disable encrypted passwords for the C<AdminUser>.

=item MV_PAGE

Name of the last page read in, as in the page called with mv_nextpage
or mv_orderpage

=item CURRENCY

The current locale for currency

=item LANG

The current locale for language

=back

=head2 VarName

Sets the names of variables that will be remapped to and from the URL when
MiniVend writes it. For instance, if you wanted the variable mv_session_id
to display as C<session> in the users URL:

    VarName   mv_session_id  session

The default can also be set in the C<etc/varnames> file after the first
time MiniVend is run. Setting it in minivend.cfg is probably better for
clarity.

There is also a catalog-specific version of this setting.

=head1 CATALOG.CFG

If multiple catalogs are to be run, each must have a C<catalog.cfg>
file located in the catalog base directory. It contains most of
the configurable parameters for MiniVend -- each is independent
from catalog to catalog.

In the catalog configuration file, the directives MailOrderTo
and VendURL are B<required>. They are not defined with defaults,
and no catalog will operate unless and until they are set with
a value.

Many powerful procedures are available in the catalog.cfg file.
First, you can set a C<Variable> and use its results in a subsequent
configuration setting if C<ParseVariables> is on:

    Variable   SERVER_NAME  www.minivend.com
    Variable   CGI_URL      /cgi-bin/demo
  
    ParseVariables Yes
    VendURL    http://__SERVER_NAME____CGI_URL__
    ParseVariables No

You can define values in multiline form by using the <<HERE document
method:

    Variable MYSTUFF <<EOF
        This is my stuff, and I
        am pretty proud of it.
    EOF

As with all here documents, the terminating string must be the ONLY THING
ON THE LINE. No leading or trailing characters are allowed, even whitespace.

=over 4

=item Include single setting from file

You can pull a value from a file with <file:

    Variable MYSTUFF <file

This works well for includes that must be of the highest possible 
performance -- they can be simply placed in a page with __VARIABLE__.

=item Include multiple settings from file

You can set common settings in one file:

    #include common.cfg

Or all files in one directory

    #include usertag/*

=item #ifdef and #ifndef

You can use #ifdef / #endif and #ifndef / #endif pairs:

    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@localhost
    #endif

=item Define subroutine watches

You can set up almost any configuration variable to be tied to a subroutine
if you have the Tie::Watch module installed. It uses a notation like the
<<HERE document, but <&HERE is the notation. See C<MiniVend Programming>
for details.

=back

=head2 Programming watch points in catalog.cfg

You can set up almost any configuration variable to be tied to a subroutine
if you have the Tie::Watch module installed. It uses a notation like the
<<HERE document, but <&HERE is the notation. See C<MiniVend Programming>
for details.

Here is a simple case:

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

When the order is mailed out, if the user has a variable called
C<special_handling> set in their session (from UserDB, perhaps) then the order
will be sent to 'vip@minivend.com'. (Note the single quotes to prevent problems
with the @ sign.) Otherwise, they will get sent to the previously defined value
of C<orders@minivend.com>.

If the configuration value being watched is a SCALAR, the subroutine
gets the following call:

    &{$subref}(SELF, PREVIOUS_VALUE)

The subroutine should simply return the proper value.

SELF is a reference to the Tie::Watch object (read its documentation for what
all it can do) and PREVIOUS_VALUE is the I<previously> set value for the 
directive. (If you set it after you set up the watch, it will simply have
the effect of destroying the watch and having unpredictable effects. In the
future, you may be able to set up a "Store" routine that can subsequently
set values).

If the configuration value being watched is an ARRAY, the subroutine
gets the following call:

    &{$subref}(SELF, INDEX, PREVIOUS_VALUE)

INDEX is the index of the array element being accessed.  Setting up watch
points on array values is not recommended -- most MiniVend subroutines
call arrays in their list context, and no access method is provided
for that.

If the configuration value being watched is a HASH, the subroutine
gets the following call:

    &{$subref}(SELF, KEY, PREVIOUS_VALUE)

KEY is the index into the hash. An example of HASH type MiniVend configuration
values 

NOTE: The following is not recommended for performance reasons -- the Variable
is a commonly used thing and should not bear the extra overhead -- but it illustrates
the power quite nicely:

    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->{name}";
            }
        }
        else {
            return $orig->{$key};
        }
    }
    EOV

The first time __TESTIT__ is called for a particular user, it will return
the string "Tie::Watch works! -- name=" along with their name set in
the session (if that exists). Any other variables will receive the value
that they were set to previously. Once the TESTIT key has been accessed
for that user, the watch is dropped upon the next access.

=head1 Configuration Directives in catalog.cfg

All directives except I<MailOrderTo> and I<VendURL> have default values
and are optional, though most catalogs will want to configure some
of them.

=head2 ActionMap

Allows setting of MiniVend actions, usually with a Perl subroutine
of your choice. Actions are page names like:

 process  Perform a processing function
 order    Order items
 scan     Search based on path info
 search   Search based on submitted form variables

These are the standard supplied actions for MiniVend. You can overwrite
them with your own version if you wish. For instance, if you wanted to
ignore the C<order> action, you could do:

    ActionMap  order  sub { return 1 }

When the leading part of the incoming path is equal to C<order>, it will
trigger your action. The page name will be shifted up, and the C<order> 
stripped from the page name. So this custome C<order> action
would essentially perform a no-op, and a URL like:

    <A HREF="[area order/nextpage]"> Go to the next page </A>

would be the equivalent of "[area nextpage]". If the action does not
return a true (non-zero, non-blank) status then no page will be displayed
by MiniVend, not even the special C<missing> page. You can of course generate
your own response if you wish via Perl or MVASP.

The standard C<process> action has a number of associated C<FormAction> settings.

Besides using Perl, you can use MML tags in an action, though that is not
nearly as efficient.

=head2 AlwaysSecure

Determines whether checkout page operations should always be
secure. Set it to the pages that should always be secure, separated
by spaces and/or tabs.

    AlwaysSecure    ord/checkout

=head2 AsciiTrack

A file name to log formatted orders in. Unless preceded by a leading '/',
will be placed relative to the catalog directory. Disabled by default.

    AsciiTrack     etc/tracking.asc

If you have a C<Route> set up to C<supplant>, then this is ignored.

=head2 AutoModifier

Sets an attribute in a shopping cart entry to the field of the same name
in the ProductsFile pertaining to this item.

This is useful when doing shipping calculations or other embedded Perl
that is based on item attributes. To set whether an item is defined as
"heavy" and requires truck shipment, you can do:

    AutoModifier  heavy

When an item is added to the shopping cart using MiniVend's routines, the
C<heavy> attribute will be set to the value of the C<heavy> field in
the products database. In the default demo that would be C<products>; any
changes to C<ProductFiles> would affect that, of course.

Some values are used by MiniVend and are not legal:

        mv_mi
        mv_si
        mv_ib
        group
        code 
        quantity
        item

=head2 Autoload

Sets an action that is automatically performed for every access. It is
performed before any page parsing occurs, and before the action or page
is even determined.

As an example, if you wanted to remap any mv_nextpage accesses to the
C<private> subdirectory of pages, you might do:

    Autoload   [perl] $CGI->{mv_nextpage} =~ s:^private/:public/:; [/perl]

=head2 CommonAdjust

Settings for MiniVend pricing. See C<Chained pricing>.

    CommonAdjust    pricing:q2,q5,q10,q25, ;products:price, ==size:pricing

=head2 ConfigDir

The default directory where directive values will be read from
when using the <file notation. Default is C<config>. The name is
relative to the catalog directory unless preceded by a /.

    ConfigDir      variables

You can change this several times in the C<catalog.cfg> file to pick
up values from more than one directory. Another possibility is to
use a Variable setting to use different templates based on a setting:

    Variable   TEMPLATE   blue

    ParseVariables Yes
    ConfigDir  templates/__TEMPLATE__
    ParseVariables No
    Variable   MENUBAR   <menubar
    Variable   LEFTSIDE  <leftside
    Variable   BOTTOM    <bottom
    ConfigDir config

This will pick the C<templates/blue> template -- if you set TEMPLATE to 
C<red>, it would read the variables from C<templates/red>.

=head2 CookieDomain

Allows you to set a domain so that multiple servers can handle traffic.
For example, if you wanted to use server addresses of B<secure.yourdomain.com>
and B<www.yourdomain.com> then you could set it to:

    CookieDomain    .yourdomain.com

More than one domain can be set. It B<must> have at least two periods
or browsers will ignore it. (You wouldn't want a cookie being set to .com, would
you?)

=head2 CookieLogin

Allows users to save their username/password (for Vend::UserDB)
in a cookie. Expiration set by SaveExpire, renewed every time
they log in. To cause the cookie to be generated originally,
the CGI variable C<mv_cookie_password> or C<mv_cookie_username>
must be set. The former causes both username and password to be
saved, the latter just the username.

    CookieLogin  Yes

Default is C<No>.

=head2 Cookies

Determines whether we will send (and read back) a cookie to
get the session ID for links that go outside the catalog.
Allows arbitrary HREF links to be placed in MiniVend pages, while
still saving the contents of the session.  The default is B<Yes>.

    Cookies         Yes

If the I<Cookies> directive is enabled, and mv_save_session is set
upon submission of a user form (or in the CGI variables through a perl
I<GlobalSub>), the cookie will be persistent for the period defined
by I<SaveExpire>.

NOTE: You almost always want this to be "Yes".

Caching, timed builds, and static page building will never be in effect unless
this directive is enabled.

=head2 CreditCardAuto

If set to I<Yes>, enables the automatic encryption and saving of
credit card information. In order for this to work properly, the
EncryptProgram directive must be set to properly encode the field. The
best way to set EncryptProgram is with PGP in the ASCII armor mode.

This option uses the following standard fields on MiniVend order
processing forms:

=over 4

=item C<mv_credit_card_number>

The actual credit card number, which will be wiped from memory after
checking to see if it is a valid Amex, Visa, MC, or Discover card number.
This variable will never be carried forward in the user session.
 

=item C<mv_credit_card_exp_all>

The expiration date, as a text field in the form MM/YY (will take a four-digit
year as well). If it is not present, the fields C<mv_credit_card_exp_month> and
C<mv_credit_card_exp_year> are looked at.  It is set by MiniVend when the card
validation returns, if not previously set.
 

=item C<mv_credit_card_exp_month>

The expiration date month, used if the C<mv_credit_card_exp_all> field is not
present.  It is set by MiniVend when the card validation returns, if not
previously set.
 

=item C<mv_credit_card_exp_year>

The expiration date year, used if the C<mv_credit_card_exp_all> field is not
present.  It is set by MiniVend when the card validation returns, if not
previously set.
 

=item C<mv_credit_card_error>

Set by MiniVend to indicate the error if the card does not validate properly.
The error message is not too enlightening if validation is the problem.
 

=item C<mv_credit_card_force>

Set this value to 1 to force MiniVend to encrypt the card despite its idea of
validity. Will still set the flag for validity to 0 if the number/date does not
validate. Still won't accept badly formatted expiration dates.

=item C<mv_credit_card_separate>

Set this value to 1 to cause MiniVend encrypt only the card number and not
accompany it with the expiration date and card type.

=item C<mv_credit_card_info>

Set by MiniVend to the encrypted card information if the card validates
properly. If PGP is used in ASCII armor mode, this field can be placed on the
order report and embedded in the order email, replete with markers. This allows
a secure order to be read for content, without exposing the credit card number
to risk.

=item C<mv_credit_card_valid>       

Set by Minivend to true, or 1, if the the card validates properly. Set to 0
otherwise.

=back

PGP is recommended as the encryption program, though you should remember
that US commercial organizations may require a license for RSA. MiniVend
will work with GPG, the Gnu Privacy Guard.

    CreditCardAuto     Yes

This should be turned off if using C<CyberCash>.

=head2 CustomShipping

If not blank, causes an error log entry if the shipping file entry is
not found. Not otherwise used for shipping; see I<SHIPPING> for how
you go about doing that.

    CustomShipping      Yes

=head2 CyberCash

A yes/no directive, default no. Enables CyberCash payment protocols --
see I<Using CyberCash>.

    CyberCash           Yes

=head2 Database

Definition of an arbitrary database, in the form "Database database file
type", where "file" is the name of an ASCII file in the same format as
the products database. The file is relative to VendRoot, and is put in
I<DataDir> if no path is provided. Records can be accessed with the
C<[data database field key]> tag.  Database names are restricted to the
alphanumeric characters (including the underscore), and it is recommended
that you always use all lower or all upper case. See I<DATABASES>.

    Database      reviews  reviews.txt  CSV

=head2 DefaultShipping

This sets the default shipping mode by initializing the variable
C<mv_ship_mode>. If not set in F<catalog.cfg>, it is I<default>.

    DefaultShipping     UPS

Somewhat deprecated, as you can achieve the same thing with

    ValuesDefault   mv_shipmode UPS

=head2 DescriptionField

The field that will be accessed with the C<[item-description]> element.

    DescriptionField    description

Default is C<description>.  It is not a fatal error if this field
does not exist.

This is especially important for on-the-fly items; if there is an
attribute set to the same name as C<DescriptionField> this will be
used for display.

=head2 DisplayErrors

If the administrator has enabled I<DisplayErrors> globally, then setting
this to "Yes" will display the error returned from MiniVend in case something
is wrong with embedded Perl programs, tags, or (horrors!) MiniVend itself.
Usually you will only want to use this during development or debugging --
default is B<no>.

    DisplayErrors       Yes

=head2 DynamicData

When set to one or more MiniVend database identifiers, any pages using data
items from the specified database(s) will not be cached or built
statically. This allows dynamic updating of certain arbitrary (or
even product) databases while still allowing static/cached page performance
gains on pages not using those data items.

    DynamicData         inventory

Overridden by C<[tag flag build][/tag]>, depending on context.

=head2 EncryptProgram

Contains a program command line specification that indicates how an
external encryption program will work. Two placeholders, C<%p> and
C<%f>, are defined, which are replaced at encryption time with the
password and temporary file name respectively. See I<Order Security>.

This is separate from the I<PGP> directive, which enables PGP encryption
of the entire order.

If PGP is the encryption program (MiniVend determines this by searching
for the string C<pgp> in the command string), no password field or file
field need be used -- the field mv_credit_card_number will never be
written to disk in that case.

    EncryptProgram      /usr/local/bin/pgp -feat sales@company.com

If you use order C<Route> method of sending orders (default in the
demo) this sets the default value of the C<encrypt_program> attribute.

=head2 ErrorFile

This is where MiniVend will write its runtime errors for THIS CATALOG
ONLY. It can be shared with other catalogs or the main MiniVend error
log, but if you make it root-based, be careful that you have permission to
write the file, or bad things will happen.

    ErrorFile   /home/minivend/error.log

=head2 ExtraSecure

Disallows access to pages which are marked with AlwaysSecure unless
the browser is in HTTPS mode. A yes/no directive -- default is 'No'.

    ExtraSecure  Yes

=head2 FormAction

Allows you to set up a form action (like the standard ones
C<return, submit, refresh,> etc.). It requires a Perl subroutine
as a target:

    FormAction foo <<EOR
    sub {
        $CGI->{mv_nextpage} = 'bar';
    }
    EOR

If it returns a true (non-zero, non-empty) value, MiniVend will display the
page defined in $CGI->{mv_nextpage}. Otherwise, MiniVend will not display
any page (assuming you took care of that in your routine).

You can override the default MiniVend actions if you desire.

There is also a global version of this directive, which is overridden
if a catalog-specific action exists.

=head2 FormIgnore

Set to the name(s) of variables that should not be carried in the
user session values. Must match exactly and are case sensitive.

    FormIgnore    mv_searchtype


=head2 FractionalItems

Whether items in the shopping cart should be allowed to be
fractional, i.e. 2.5 or 1.25. Default is no.

    FractionalItems     Yes

=head2 Glimpse

The pathname for the glimpse command, used if glimpse searches
are to be enabled. If you wish to use C<glimpseserver>, you
must include the C<-C>, C<-J>,  and C<-K> tags if they are needed.

    Glimpse  /usr/local/bin/glimpse -C -J srch_engine -K2345

=head2 HTMLsuffix

The file extension that will be seen as a page in the C<pages> directory.
Default is C<.html>. 

    HTMLsuffix .htm

=head2 ImageAlias

Aliases for images, ala Apache/NCSA ScriptAlias and Alias directives.
Relocates images based in a particular directory to another for MiniVend
use -- operates I<after> ImageDir. Useful for editing MiniVend pages
with an HTML editor. Default is blank.

    ImageAlias  /images/  /thiscatalog/images/

=head2 ImageDir

The directory where all I<relative> IMG and INPUT source file
specifications are based. IT MUST HAVE A TRAILING / TO WORK. If the
images are to be in the DocumentRoot (of the HTTP server or virtual
server) subdirectory I<images>, for example, you would use the ImageDir
specification '/images/'. This would change SRC="order.gif" to
SRC="/images/order.gif" in IMG and INPUT tags. It has no effect on
other SRC tags.

    ImageDir /images/

Can be set in the I<Locale> settings to allow different image
sets for different locales (MV3.07 and up).

=head2 ImageDirInternal

A value for I<ImageDir> only when the internal HTTP server is in use.
It must have a trailing / to work, and should always begin with a 
fully-qualified path starting with C<http://>.

    ImageDirInternal http://www.server.name/images/

=head2 ImageDirSecure

A value for I<ImageDir> only the pages are being served via HTTPS.
It must have a trailing / to work, and should always begin with a 
fully-qualified path starting with C<http://>.

    ImageDirSecure   /secure/images/

This is useful if you are using a separate HTTPS and HTTP server,
and cannot make the image directory path heads match.

=head2 Locale

Sets the special locale array. Tries to use POSIX setlocale based
on the value of itself, then tries to accept a custom setting with
the proper definitions of C<mon_decimal_point>, C<thousands_sep>, and C<frac_digits>,
which are the the only international settings required. Default if not
set is to use US-English settings.

Example of the custom setting:

    Locale     custom mon_decimal_point , mon_thousands_sep . frac_digits 0

Example of POSIX setlocale for France, if properly aliased:

    Locale     fr

See setlocale(3) for more information. If embedded Perl code
is used to sort search returns, then the setlocale() will carry
through to string collation.

See I<Internationalization>.

=head2 LocaleDatabase

Set to the MiniVend database I<identifier> of a table that contains
Locale settings. These settings add on to and overwrite any that are
set in the catalog configuration files, including any #include files.

    Database       locale  locale.asc  TAB
    LocaleDatabase locale

=head2 MailOrderTo

Specifies the email address to mail completed orders to.

        MailOrderTo  orders@xyzcorp.com

If you specify 'none', no emailed order will be sent.

=head2 NoCache

The names of MiniVend pages that are not to be built statically if
I<STATIC PAGE BUILDING> is in use.  If the name is a directory, then no
pages in that directory (or any below it) be cached or built statically.

    NoCache    ord
    NoCache    special

=head2 NoImport

When set to one or more MiniVend database identifiers,
those database(s) will never be subject to import. Useful for SQL
databases, or databases that will *never* change.

    NoImport   inventory

=head2 NonTaxableField

The name of the field in the products database that is set (to 1 or yes)
if an item is not to be taxed. Will log an error and tax it anyway
if the field doesn't exist in the database. Blank by default, disabling
the feature.

    NonTaxableField    wholesale

=head2 OfflineDir

The location of the offline database files for use with the MiniVend
offline database build command. Set to "offline" as the default, and
is relative to VendRoot if there is no leading slash.

    OfflineDir          /usr/data/minivend/offline

=head2 OnFly

Enables on-the-fly item additions to the shopping cart. If set to
the name of a valid UserTag, that tag definition will be used to
parse and format the item with the following call:


    $item = Vend::Parse::do_tag($Vend::Cfg->{OnFly},
                                    $code,
                                    $quantity,
                                    $fly[$j],
                                );

$fly[$j] is the value of C<mv_order_fly> for that item.
An C<onfly> tag is provided by MiniVend -- see <On-the-fly> ordering.

=head2 OrderCounter

The name of the file (relative to catalog root if no leading /) that
maintains the order number counter. If not set, the order will be
assigned a string based on the time of the order and the user's
session number.

    OrderCounter       etc/order.number

Bear in mind that MiniVend provides the order number as a convenience
for display, and that no internal functions depend on it. You may define
and use your own order number routines without fear of consequences.

If you have a C<Route> set up to C<supplant> and the C<counter> attribute
is set there, then this is ignored. 

=head2 OrderLineLimit

The number of items that the user is allowed to place in the shopping
cart.  Some poorly-mannered robots may "attack" your site by following
all links one after another. Some even ignore any C<robots.txt> file you
may have created.  If one of these bad robots orders several dozen or more
items, then the time required to save and restore the shopping cart from
the user session may become excessive.

If the limit is exceeded, then the command defined in the Global directive
I<LockoutCommand> will be executed and the shopping cart will be emptied.
The default is 0, disabling the check.  Set it to a number greater than
the number of line items you ever expect a user to order.

    OrderLineLimit   50

=head2 OrderProfile

Allows an unlimited number of profiles to be set up, specifying
complex checks to be performed at each of the steps in the checkout process.
The files specified can be located anywhere -- if relative paths
are used, they are relative to the catalog root directory.

    OrderProfile    etc/profiles.order etc/profiles.login

The actions defined here are also used for C<mv_click> actions if there
is no action defined in C<scratch> space.

They are accessed by setting the C<mv_order_profile> variable to the
name of the order profile. Multiple profiles can
reside in the same file, if separated by __END__ tokens, which must be
on a line by themselves.

The profile is named by placing a name following a __NAME__
pragma:

  __NAME__ billing

The __NAME__ must begin the line, and be followed by whitespace
and then the name. The search profile can then be accessed by
F<mv_order_profile="billing">. See I<Advanced Multi-level Order Pages>.

=head2 OrderReport

The location of the simple order report file. Defaults
to I<etc/report>. 

    OrderReport          /data/order-form

=head2 PageDir

Location of catalog pages. Defaults to the pages subdirectory in the
VendRoot directory.

    PageDir    /data/catalog/pages

Can be set in the I<Locale> settings to allow different page
sets for different locales.

=head2 PageSelectField

Sets a products database column which can be used to select the
on-the-fly template page. This allows multiple on-the-fly pages to
be defined. If the field is empty (no spaces, either!) then the default
<flypage> will be used.

    PageSelectField    display_page

=head2 ParseVariables

Determines whether global and catalog variables will be parsed in the
configuration file. Should be set to No until parsing is needed,
turned on for the parsed directives, then set back to No.  Default
is No.

    Variable STORE_ID  topshop
    ParseVariables Yes
    StaticDir  /home/__STORE_ID__/www/cat
    ParseVariables No

=head2 Password

The encrypted or unencrypted password (depending on Variable MV_NO_CRYPT)
that will cause internal authorization checks for RemoteUser to allow access.

Below is the encrypted setting for a C<blank> password.

    Password                bAWoVkuzphOX.

=head2 PGP

If credit card information is to be accepted, and the emailed order will
go over an insecure network to reach its destination, PGP security should
be used. The key ring to be used must be for the user that is running the
MiniVend server, or defined by the environment variable PGPPATH, and the
key user specified must have a key on the public key ring of that user.

    PGP        /usr/local/bin/pgp -feat orders@company.com

If this directive is non-null, the PGP command string as specified will be used
to encrypt the I<entire order> -- in addition to any encryption done as a
result if I<CreditCardAuto>, If for some reason an error comes from PGP, the
customer will be given the special page C<failed>.

If you have a C<Route> set up to C<supplant> then this is ignored. 

=head2 Pragma

Sets the default value of a MiniVend pragma. The only one to date
is: 

=over 4

=item no_html_parse

Disallows HTML tag parsing. This is a BIG parser performance gain and is
enabled in the demo catalog. 

=back

If you want to enable a pragma for only a particular page, do anywhere
in the page:

    [pragma no_html_parse]

If you want to disable a pragma for a particular page, do anywhere in the
page:

    [pragma no_html_parse 0]

If you know what a DTD is, that is a good place to put this.

=head2 PriceCommas

If you desire no commas in your price numbers (for the C<[item-price]> tag)
set this to C<no>. The default is to use commas (or whatever is
the thousands separator for your locale).

    PriceCommas         no

This is overridden if a I<Locale> C<price_picture> is set.

=head2 PriceDivide

The number the price should be divided by to get the price
in units (dollars or such). The default is one -- if you use
penny pricing you can set it to 100.

    PriceDivide         100

Can be set in the I<Locale> settings to allow a price
adjustment factor for different currencies.

=head2 PriceField

The field in the product database that will be accessed with
the C<[item-price]> element. Default is "price".

    PriceField          ProductPrice

Can be set in the I<Locale> settings to allow different price
fields for different currencies.

=head2 ProductDir

Location of the database files. Defaults to the products subdirectory of
the VendRoot directory. May not be set to an absolute directory unless
NoAbsolute is defined as No.

    ProductDir          /data/catalog/for-sale

Most people never set this directive and use the default of
C<products>.

=head2 ProductFiles

Database tables that should be seen as the "products" database.

    ProductFiles    vendor_a vendor_b

The key thing about this is that each will be searched in sequence
for a product code to order or an C<[item-field ....]> or C<[loop-field ...]>
to insert.  The main difference between C<[item-field ....]> and 
C<[item-data table ...]> is this fall-through behavior.

Default is C<products>.

=head2 ReadPermission and WritePermission

By default, only the user account that MiniVend runs under (as set by the
setuid permission on vlink) can read and write files created by MiniVend.
WritePermission and ReadPermission can be set to C<user>, C<group>, or
'world'.

    ReadPermission      group
    WritePermission     group

=head2 RemoteUser

The value of the HTTP environment variable REMOTE_USER that will
enable catalog reconfiguration. You need to enable HTTP basic
authentication for this to work. Default is blank, disabling this
check.

    RemoteUser   minivend

=head2 Replace

Causes a directive to be emptied and re-set (to its default if no
value is specified). Useful for directives that add to the value
by default.

    Replace NoCache ord special multi reconfig query

Capitalization must be exact on the directive.

=head2 Require

Forces a global UserTag or GlobalSub to be present before the catalog
will configure. This is useful when transporting catalogs to make sure
that you will have all needed facilities.

    Require usertag   email
    Require globalsub form_mail

=head2 RobotLimit

The RobotLimit directive defines the number of consecutive pages a user
session may access without a 30 second pause.  If the limit is exceeded,
then the command defined in the Global directive I<LockoutCommand>
will be executed. The default is 0, disabling the check.

    RobotLimit  200

=head2 Route

Sets up order routes. See I<Custom Order Routing>; there are examples
in the demo C<simple>.

=head2 SalesTax

If non-blank, enables automatic addition of sales tax based on the order
form. The value is a comma-separated list of the field names (as placed
in order.html), in priority order, which should be used to look up sales
tax percentage in the salestax.asc database. This database is not
supplied with MiniVend -- it is typically received from a third party by
quarterly or monthly subscription.

    SalesTax            zip state

=head2 SalesTaxFunction

A Perl subroutine that will return a hash reference with the sales tax
settings. This can be used to query a database for the tax for a particular
vendor:

    SalesTaxFunction  <<EOR
    sub {
        my $vendor_id = $Session->{source};
        my $tax = $TextSearch->hash( {
                            se => $vendor_id,
                            fi => 'salestax.asc',
                            sf => 'vendor_code',
                            ml => 1000,
                            } );
        $tax = {} if ! $tax;
        $tax->{DEFAULT} = 0.0;
        return $tax;
    }
    EOR

or simply produce a table:

    SalesTaxFunction  <<EOR
    sub {
        return {
            DEFAULT => 0.0,
            IL => 0.075,
            OH => 0.065,
        };
    }
    EOR

A C<DEFAULT> value must always be returned or the function will be
ignored.

=head2 SaveExpire

The default amount of time that a cookie will be valid for (other than
the MV_SESSION_ID cookie). The ones used in MiniVend by default are
MV_USERNAME and MV_PASSWORD, for the I<CookieLogin> feature.

Specified the same as SessionExpire, with an integer number followed
by one of C<minutes> or C<hours> or C<days> or C<weeks>.

    SaveExpire 52 weeks

Default is C<30 days>.

=head2 ScratchDefault

The default scratch variable settings the user will start with when
their session is initialized. To disable placing URL rewrite strings
after the user has given a cookie, you can do:

    ScratchDefault  mv_no_session_id  1
    ScratchDefault  mv_no_count       1
    ScratchDefault  mv_add_dot_html   1

=head2 ScratchDir

The directory where temporary files will be written, notably cached
searches and retired session IDs. Defaults to C<tmp> in the catalog
directory.

    ScratchDir          /tmp

=head2 SearchProfile

Allows an unlimited number of search profiles to be set up, specifying
complex searches based on a single click. The directive accepts a file
name, based in the catalog directory if the path is relative:

    SearchProfile    etc/search.profiles

As an added measure of control, the specification is evaluated with the
special MiniVend tag syntax to provide conditional setting of search
parameters.

The following file specifies a dictionary-based search in the file
'dict.product':

  __NAME__ dict_search
  mv_search_file=dict.product
  mv_return_fields=1
  [if value fast_search]
    mv_dict_limit=-1
    mv_last=1
  [/if]
  __END__

The __NAME__ is the value you will specify in the mv_profile variable
on the search form, as in

  <INPUT TYPE=hidden NAME=mv_profile VALUE="dict_search">

or with mp=profile in the one-click search.

  [page scan se=Renaissance/mp=dict_search]Renaissance Art[/page]

Multiple profiles can reside in the same file, if separated by __END__
tokens. __NAME__ tokens should be left-aligned, and __END__ must be on
a line by itself with no leading or trailing whitespace.

=head2 SecureURL

The base URL for secure forms/page transmissions. Normally it is
the same as I<VendURL> except for the C<https:> protocol definition.
Default is blank, disabling secure access.

    SecureURL   https://machine.com/xyzcorp/cgi-bin/vlink

=head2 SendMailProgram

The location of the sendmail binary, needed for mailing orders.
Must be found at startup. This often needs to be set for FreeBSD
or BSDI.

   SendMailProgram    /usr/sbin/sendmail

If set to C<none>, no mail can be sent by standard MiniVend facilities.

The default is the value in C<MINIVEND.CFG> and varies depending on
operating system.

=head2 SeparateItems

Changes the default when ordering an item via MiniVend to allowing multiple
lines on the order form for each item. The default, C<No>, puts all
orders with the same part number on the same line.

Setting SeparateItems to C<Yes> allows the item attributes to be
easily set for different instances of the same part number, allowing
easy setting of things such as size or color.

    SeparateItems       Yes

Can be overridden with the F<mv_separate_items> variables (both
scratch and values).

=head2 SessionDatabase

When storing sessions, specify the name of the directory or DBM file
to use. The file extensions of .db, or .gdbm (depending on the DBM
implementation used) will be appended; if the default file-based sessions
are used, then it is the name of the directory.

    SessionDatabase     session-data

Can be an absolute path name if desired.

It is possible for multiple catalogs to share the same session file, and
very possible for multiple MiniVend servers to serve the same catalogs.
If you are serving a massively busy store, multiple parallel MiniVend
servers can share the same NFS-based file system and serve users in a
"ping-pong" fashion using the file-based sessions. On very, very, large
systems you might want to change the level of directory hashing -- by 
default only 48 * 48 hashing is done. See the source for SessionFile.pm --
if you can't find the setting you have no business running that large
a site. 8-)

=head2 SessionDB

The name of the MiniVend database to be used for sessions if DBI
is specified as the session type. B<You don't want to use this>.

=head2 SessionExpire

A customer can exit their browser or leave the catalog pages at any time,
and no indication is given to the HTTPD server aside from the lack of
further requests that have the same session id. Old session information
needs to be periodically expired. The SessionExpire specifies the
minimum time to keep track of session information. Defaults to one day.
Format is an integer number, followed by s(econds), m(inutes), h(ours),
d(ays), or w(eeks).

    SessionExpire       20 minutes

If C<CookieLogin> is in use, this can be a small value.

=head2 SessionLockFile

The file to use for locking coordination of the sessions.

    SessionLockFile     session-data.lock

This only applies when using DBM-based sessions.  It is possible
for multiple catalogs to share the same session file.  You will also
need to set I<SessionDatabase> appropriately if the database is to be
shared. Defaults to C<session.lock>, which is appropriate for separate
session files (and therefore standalone catalogs). Can be an absolute
path name if desired.

=head2 SessionType

The type of session management to be used; one of:

    DB_File     Berkeley DB
    DBI         DBI (don't use this!)
    File        File-based sessions (the default)
    GDBM        GDBM

The default is file-based sessions, which provides the best performance and
reliablility in most environments.

=head2 SpecialPage

Sets a special page to other than its default value. Can be set as
many times as necessary -- will have no effect if not one of the
MiniVend I<Required Pages>.

    SpecialPage         checkout ord/checkout
    SpecialPage         failed special/error_on_order
    SpecialPage         interact special/browser_problem
    SpecialPage         noproduct special/no_product_found
    SpecialPage         order  ord/basket
    SpecialPage         search srch/results

=head2 SpecialPageDir

The directory where special pages are kept. Defaults to C<special_pages>
in the catalog directory.

    SpecialPageDir      pages/special

=head2 Static

A yes/no directive.
Enables static page building and display features. Default is C<no>.

     Static   Yes

=head2 StaticAll

A yes/no directive.
Tells MiniVend to try and build all pages in the catalog statically
when called with the static page build option. This is subject to the
settings of I<StaticFly>, I<StaticPath>, and I<NoCache>. Default is No.
(Of course pages that have dynamic elements will not be built statically,
though that may be overridden with C<[tag flag build][/tag]> on the page
in question.)

     StaticAll   Yes

=head2 StaticDepth

The number of levels of static search building that will be done
if a search results page contains a search. Default is one -- beware
that it could be very long if you set it higher. Set to 0 to disable
re-scanning of search results pages.

     StaticDepth 2

=head2 StaticDir

The absolute path of the directory which should be used as the root
for static pages. The user ID executing MiniVend must have write permission
on the directory (and all files within) if this is to work.

     StaticDir   /home/you/www/catalog

=head2 StaticFly

A yes/no directive. If set to C<Yes>, static builds will attempt to generate
a page for every part number in the database using the on-the-fly page build
capability. If pages are already present with those names, they will be
overwritten. The default is C<No>.

     StaticFly   Yes

=head2 StaticPage

Tells MiniVend to build the named page (or pages, whitespace separated)
when employing the static page-building capability of MiniVend. Not necessary
if using StaticAll.

     StaticPage   info/about_us  info/terms_and_conditions

=head2 StaticPath

The path (relative to HTTP document root) which should be used
in pages built with the static page-building capability of MiniVend.

     StaticPath    /catalog

=head2 StaticPattern

A perl regular expression which is used to qualify pages that are to
be built statically. The default is blank, which means all pages qualify.

     StaticPattern  ^info|^help

=head2 StaticSuffix

The extension to be appended to a normal MiniVend page name when building
statically. Default is C<.html>.  Also affects the name of pages in the
MiniVend page directory -- if set to .htm the pages must be named with that
extension.

     StaticSuffix   .htm

=head2 Sub

Defines a catalog subroutine for use by the C<[perl][/perl]>
or [mvasp] embedded perl languages. Use the "here document" capability of MiniVend
configuration files to make it easy to define:

    Sub <<EOF
    sub sort_cart_by_quantity {
        my($items) = @_;
        $items = $Items if ! $items;
        my $out = '<TABLE BORDER=1>';
        @$items = sort { $a->{quantity} <=> $b->{quantity} } @$items;
        foreach $item (@$items) {
            my $code = $item->{code};
            $out .= '<TR><TD>';
            $out .= $code;
            $out .= '</TD><TD>';
            $out .= $Tag->data('products', 'name', $code);
            $out .= '</TD><TD>';
            $out .= $Tag->data('products', 'price', $code);
            $out .= '</TD></TR>';
        }
        $out .= '&lt/TABLE>';
        return $out;
    }
    EOF

As with Perl "here documents", the EOF (or other end marker) must be the
ONLY thing on the line, with no leading or trailing white space. Do not
append a semicolon to the marker.

The above would be called with:

    [perl]
        my $cart = $Carts->{main};
        return sort_cart($cart);
    [/perl]

and will display an HTML table of the items in the current shopping cart,
sorted by the quantity. Syntax errors will be reported at catalog
startup time. 

Catalog subroutines may not perform unsafe operations -- the I<Safe.pm>
module enforces this unless global operations are allowed for the 
catalog. See C<AllowGlobal>.

=head2 TaxShipping

A comma or space separated list of states or jurisdictions that
tax shipping cost, i.e. UT. Blank by default, never taxing shipping.

    TaxShipping         UT,NV,94024

=head2 UpsZoneFile

The file containing the UPS zone information,  specified relative to
the catalog directory unless it begins with a /. It can be in the format
distributed by UPS (this is true until the year 2000 at least) -- or can
be in a tab-delimited format, with the three-letter zip prefix of the
customer used to determine the zone. It interpolates based on the value
in C<mv_shipmode>. A user database B<named the same as the mv_shipmode
variable> must be present or the lookup will return zero.

<IMPORTANT NOTE:> You must obtain the zone information and updated
pricing from UPS in order for this to work properly. The zone information
is specific to your region!

    UpsZoneFile         /usr/minivend/data/ups_zone.asc

=head2 UseModifier

Determines whether any attributes, the modifiers specified in the
directive, can be attached to the item. See I<Item Attributes>. The
default is no modifier. Don't use a value of C<quantity> -- it will not
do what you want.

    UseModifier         size,color

Some values are used by MiniVend and are not legal:

        mv_mi
        mv_si
        mv_ib
        group
        code 
        quantity
        item

=head2 ValuesDefault

Sets the initial state of the user values, i.e. [value key] or $Values->{key}.

    ValuesDefault   fname  New
    ValuesDefault   lname  User

When the user session starts, C<[value fname] [value lname]> will be "New User".

=head2 Variable

Defines a catalog variable that will be available in the current catalog
with the notation __Variable__. Variable identifiers must begin with
a capital letter, and can contain only word characters (B<A-Z,a-z,0-9> and
underscore).  These are substituted second (right after global Variables)
in any MiniVend page, and can contain any valid MiniVend tags except
global variables.

    Variable   DOCUMENT_ROOT   /usr/local/etc/httpd/htdocs

=head2 VariableDatabase

The name of a database containing a field Variable which will 
be used to set MiniVend variable values. For example, a database
defined as:

    Database  var var.asc TAB

and containing

    code    Variable
    HELLO   Hi!

would cause __HELLO__ to appear as C<Hi!>. 

The values set here override those set in C<catalog.cfg>.

=head2 VendURL

Specifies the base URL that will run vlink as a cgi-bin program.

        VendURL  http://machine.company.com/cgi-bin/vlink


=head2 WideOpen

Disables IP qualification of user sessions. THIS DEGRADES CATALOG SECURITY.
Do not use unless you use PGP/CreditCardAuto or CyberCash.

=head1 ADMINISTERING MINIVEND

Some utilities are supplied in the VendRoot/bin directory:

  compile_link Compiles a MiniVend vlink or tlink CGI link
  dump         Dumps the session file for a particular catalog
  expire       Expires sessions for a particular catalog
  expireall    Expires all catalogs
  offline      Does offline build of the database(s)
  update       Does in-place update of the database(s)
  makecat      Make catalog

Some example scripts for other functions are in the C<eg/> directory
of the software distribution; see the readme file there.

Some thought should be given to where the databases, error logs,
and session files should be located, especially on an ISP that
might have multiple users sharing a MiniVend server. In
particular, you might put all of the session files and logs
in a directory that is not writable by the user -- if the
directory or file is corrupted the catalog may go down.

To test the format of user catalog configuration files before
restarting the server, you can do (from VendRoot):

    bin/minivend -test

That will check all configuration files for syntax errors, which
might otherwise prevent a catalog from coming up. Once a catalog
configures properly, user reconfiguration will not crash it, just
cause an error. But it must come up when the server is started.

=head2 Starting, Stopping, and Re-starting the Servers

The following commands need to have VENDROOT changed to the
main directory where you installed MiniVend. If you made /home/minivend/mvend
your MiniVend base directory, the start command would be
C</home/minivend/mvend/bin/minivend>.

Do a C<perldoc VENDROOT/bin/minivend> for full documentation.

To start the server with default settings:

    VENDROOT/bin/minivend

It is usually best to issue a restart, otherwise the server will not
run anew if a server is already running.

    VENDROOT/bin/minivend -restart

Assuming the server starts correctly, you will see the names of
catalogs as they are configured, along with a message stating
the process ID it is running under.

To re-start the server:

    VENDROOT/bin/minivend -restart

C<-r> is the same as C<-restart>.

This is typically done to force MiniVend to re-read its configuration.
You will see a message stating that a TERM signal has been sent to the
process ID the servers are running under -- that information is also
sent to /home/minivend/error.log. Check the error.log file for
confirmation that the server has restarted properly.

To stop the server:

    VENDROOT/bin/minivend -stop

You will see a message stating that a TERM signal has been sent to
the process ID the server is running under -- that information is also sent
to /home/minivend/error.log.

Because processes waiting for selection on some operating systems block
signals, they may have to wait for I<HouseKeeping> seconds to stop. The
default is 60.

To terminate the MiniVend server with prejudice, in case it will not stop:

    VENDROOT/bin/minivend -kill

=head2 UNIX and INET modes

Both UNIX-domain and INET-domain sockets can be used for communication. INET
domain sockets are useful when more than one server, connected via a local-area
network (LAN), is used for accessing a MiniVend server.

B<IMPORTANT NOTE:> When sending sensitive information like credit card
numbers over a network, always ensure that the data is secured by a firewall,
or that the MiniVend server runs on the same machine as any SSL-based
server used for encryption.

If you only want to run with one method of communication, use the
C<->i and C<->u flags.

    # Start only in UNIX mode
    VENDROOT/bin/minivend -r -u

    # Start only in INET mode
    VENDROOT/bin/minivend -r -i

=head2 User reconfiguration

The individual catalogs can be reconfigured by the user by running
the [reconfig] support tag. This should be protected by one of the
several forms of MiniVend authentication.
preferably by HTTP basic authorization (see I<RemoteUser>).

You can reconfigure from the command line (as the MiniVend user) with:

    VENDROOT/bin/minivend -reconfig <catalog>

It is easy to manually reconfigure a catalog if you are the administrator.
MiniVend simply looks for a file C<etc/reconfig> (based in the MiniVend
software directory) at HouseKeeping time.  If it finds a script name
that matches one of the catalogs, it will reconfigure that catalog.

=head2 Making the Product Database

The DBM product databases can be built offline with the C<offline>
command. The directory to be used for output is specified either on
the command line with the C<-d> option, or is taken from the C<catalog.cfg>
directive I<OfflineDir> -- C<offline> in the catalog directory by default.
The directory must exist. The source ASCII files should be present in
that directory, and the DBM files are created there. Existing files
will be overwritten.

    offline -c catalog [-d offline_dir]

Do a C<perldoc VENDROOT/bin/offline> for full documentation.

=head2 Updating Individual Records

If you have a very large DBM database that takes a long time to build,
you may want to use the C<bin/update> script to change just one field
in a record, or to add from a corrections list.

The following updates the products database C<price> field for
item 19-202 with the new value 25.00

    update -c catalog -f price 25.00

More than one field can be updated on a single command line

    update -c catalog -f price -f comment 25.00 "That pitchfork couple"

The following takes input from C<file>, which must be
B<formatted exactly like the original database>
and adds/corrects any records contained therein.

    update -c catalog -i file

Invoke the command without any arguments for a usage message
describing the options.

=head2 Expiring Sessions

If you have DBM capability and MiniVend is using it to store the sessions,
you should periodically expire old sessions to keep the session database
file from growing too large.

    expire -c catalog

There is also an C<expireall> script which reads all catalog
entries in C<minivend.cfg> and runs C<expire> on them.

The C<expire> script accepts a C<-r> option which tells it to recover
lost disk space.

On UNIX, you could add a crontab entry such as the following:

    # once a day at 4:40 am
    40 4 * * *    perl /home/minivend/bin/expireall -r

MiniVend will wait until the current transaction is finished before
expiring, so you can do this at any time without disabling web access.
Any search paging files for the affected session (kept in I<ScratchDir>)
will be removed as well.

With Windows or other operating systems which don't fork(), you
will need to stop the server before running C<expire> -- this
will prevent corruption of the database.

If you are not running DBM sessions, you can use a perl script to delete
all files not modified in the last one or two days. The following will
work if given an argument of your B<session directory or session files>:

    #!perl
    # expire_sessions.pl -- delete files 2 days old or older

    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";
    }

It would be run with a command invocation like:

    perl expire_sessions.pl /home/you/catalogs/simple/session

You can give it multiple directory names if you have more than one catalog.

You can adjust this script to do what you wish, of course. Refinements might
include reading the file to "eval" the session reference and expire only
customers who are not members.

=head1 DEBUGGING

No debug is provided by default. You can edit the source files and 
uncomment '::logDebug(SOMETHING)' statements there, and set the
value of C<DebugFile> to a file that will be written to:

    DebugFile /tmp/mvdebug

=head1 MANUAL INSTALLATION OF CATALOGS

A MiniVend installation is complex, and requires quite a few distinct
steps. That is why there is an interactive configuration script that is
included with MiniVend -- it merely does automatically what is described
below. It makes the process much easier, and will install the demo
catalog. This configuration script has been tested on many UNIX systems.

The installation program (makecat) can be used to install your own
custom catalog template.  See the supplied demo template
C<simple> for examples.

=head2 Needed Directories

The MiniVend program, and its supporting libraries, should all
go into one directory as installed by the installation program.

User catalog pages, user databases, and user configuration files should
all go into their private directories. Because the catalog pages are
served through the MiniVend cgi-bin program and contain nonstandard
elements, they should not be put into a public WWW directory, nor do
they need to have world-readable file permissions.

B<IMPORTANT NOTE>: Since catalogs are all run under one server,
permissions are complex and very important.  Please let the MiniVend
configuration program do the work!

You will want a public WWW directory for in-line image graphic files.
MiniVend does not serve the images, only the HTML tags calling them. A
useful convention is to place all buttonbars, backgrounds, and icons in
the /images directory, with the catalog items perhaps located in the
/images/catalog directory. It is up to you, but remember that you must
use an I<absolute path> -- relative paths will not do. MiniVend 2.0
supports the I<ImageDir> directive, which places that as the
absolute path in front of all relative IMG and INPUT SRC specifications.

You will need a cgi-bin directory in which to put the vlink or tlink program.

=head1 Setup for HTTP Servers

MiniVend requires a that a web server be installed on your system in
the normal course of events; it does have an internal server which can
be used for administration, testing, and maintenance, but you will not
want to use it to serve in a production environment.

As detailed previously, MiniVend is always running in the background
as a daemon, or resident program. It monitors either a UNIX-domain
file-based socket or a series of INET-domain sockets. The small
CGI link program, called in the demo C<simple>, is run to connect
to one of those sockets and provide the link between your browser.

NOTE: Since Apache and other CERN/NCSA-derived servers are the most
popular, we will talk in the terms they use. If you use another
web server, you may have to translate the terms; for instance on
MS Personal web server the standard C<ScriptAlias> is C</scripts>.

You need to have a C<ScriptAlias> or other CGI execution capability
to use the link program. (The default C<ScriptAlias> for many web
servers is C</cgi-bin>.) If you have C<ExecCGI> set for all of your
directories, then any program ending in a particular file suffix (usually
C<.cgi>) will be seen as a CGI program.

MiniVend, by convention, names the link program the same name as the
catalog ID, though this is not required. In the distribution demo,
this would yield a program name or SCRIPT_PATH of C</cgi-bin/simple>
or C</simple.cgi>. This SCRIPT_PATH can be used to determine which MiniVend
catalog will be used when the link program is accessed.

=head2 UNIX-domain sockets

This is a socket which is not reachable from the Internet directly,
but which must come from a request on your own server. The link program
C<vlink> is the provided facility for such communication with MiniVend.

This is the most secure way to run your catalog, for there is no way
for systems on the Internet to interact with MiniVend except through
its link program.

The most important issue with UNIX-domain sockets on MiniVend is the
permissions with which the CGI program and the MiniVend server run.

To improve security, MiniVend normally runs with the socket file
having 0600 permissions (rw-------), which mandates that the CGI
program and the server run I<as the same user ID>. This means
that the C<vlink> program must be SUID to the same user ID as
the server executes under. (Or that CGIWRAP is used on a single
catalog system).

With MiniVend multiple catalog capability, the permissions situation
gets a bit tricky. MiniVend comes with a program, C<makecat>, which
configures catalogs for a multiple catalog system. It should properly set
up ownership and permissions for multiple users if run as the superuser.

=head2 INET-domain sockets

These are sockets which are reachable from the Internet directly.
The link program C<tlink> is the provided facility for such communication with
MiniVend; you may also use your browser to talk to the socket directly
if you have it mapped to a catalog with the global I<TcpMap> directive.

To improve security, MiniVend usually checks that the request comes
from one of a limited number of systems, defined in the global I<TcpHost>
directive. (This check is not made for the internal HTTP server.)

=head2 Internal HTTP server

If you contact the socket directly (only for INET-domain sockets),
MiniVend will perform the HTTP server function itself, talking directly
to the browser. It can monitor any number of ports and map them to
a particular catalog; by default it only maps the special catalog
mv_admin, which performs administrative functions. The default port
is 7786 (ASCII for M an V), which is the default compiled into the 
distribution I<tlink> program. You can change this port via the
I<TcpMap> directive.

To prevent catalogs that do not wish access made in this way from
being served from the internal server, MiniVend has a fixed SCRIPT_PATH
of C</catalogname> (/simple for the distribution demo) which needs
to be placed as an alias in the I<Catalog> directive to enable access.
See I<TcpMap> for more details.

=head2 Setting up VLINK and TLINK

The C<vlink> and C<tlink> programs, compiled from C<vlink.c> and
C<tlink.c>, are small C programs which contact and interface to a running
MiniVend daemon. The vlink executable is normally made setuid to the
user account which runs MiniVend, so that the UNIX-domain socket file
can be set to secure permissions (user read-write only). It is normally
not necessary for the user to do anything -- they will be compiled by
the configuration program. If the MiniVend daemon is not running, either
will display a message indicating that the server is not available.
The following defines in the produced C<config.h> should be set:

=over 4

=item LINK_FILE

Set this to the name of the socket file that will be used for
configuration, usually "/usr/local/lib/minivend/etc/socket" or the
"etc/socket" under the directory you chose for the VendRoot.

=item LINK_HOST

Set this to the IP number of the host which should be contacted.
The default of 127.0.0.1 (the local machine) is probably best for
many installations.

=item LINK_PORT

Set this to the TCP port number that the MiniVend server will monitor.
The default is 7786 (the ASCII codes for 'M' and 'V') and does not
normally need to be changed.

=item LINK_TIMEOUT

Set this to the number of seconds vlink or tlink should wait before
announcing that the MiniVend server is not running. The default of 45
is probably a reasonable value.

=back

=head2 Compiling VLINK and TLINK

There is a C<compile_link> program which will assist with this. Do:
    
    perldoc VENDROOT/bin/compile_link

for its documentation.

=head2 Manually compiling VLINK and TLINK

Change directories to the C<src> directory, then run the GNU
configure script:

    cd src
    ./configure

You will see some output as the configure script checks your system.
Then compile the programs:

    perl compile.pl

To compile manually:

    cc vlink.c -o vlink
    cc tlink.c -o tlink

On manual compiles, you can ensure your C compiler will be invoked
properly with this little ditty:

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

On some systems you can make the executable smaller with the strip
program. But don't worry about it if strip is not on your system.

    strip vlink
    strip tlink

If you want MiniVend to run under a different user account than your own,
make that user the owner of vlink. (You probably need to be root to do
this). Do not make vlink owned by root, because making vlink setuid
root is an huge and unnecessary security risk. It should also B<not>
normally run as the default WWW user (often C<nobody> or C<http>)).

    chown minivend vlink

Move the vlink executable to your cgi-bin directory:

    mv vlink /the/cgi-bin/directory

Make vlink setuid:

    chmod u+s /the/cgi-bin/directory/vlink

Most systems unset the SUID bit when moving the file, so you should
change it after moving.

The SCRIPT_NAME as produced by the HTTP server must match the name
of the program. (As usual, you should let the makecat program do
the work.)


=head1 AUTHOR

Mike Heins, F<mike@minivend.com>.

=head1 ACKNOWLEDGEMENTS

First, to Andrew Wilcox for his 1995 Vend, which inspired MiniVend. There are
still a few vestiges of his code in MiniVend, and it would not have happened
without it and him. Thanks, Andrew.

My wife Jean has shown immense patience with me, especially when I go into
my cave to do a new release. 8-)

There have been many who contributed to Minivend's development, especially with
testing and feature content. They include:

    Andreas Koenig     
    Bill Randle
    Birgitt Funk       
    Bob Jordan         
    Brian Bullen       
    Bruce Albrecht     
    Christian Mueller
	Christopher Thompson
    Don Grodecki       
    Frank Bonita       
    Gunnar Hellekson   
    Heinz Wittenbecher
    Keiko
    Jochen Wiedmann
    Larry Leszczynski
    Marc Austin
    Michael McCune
    Mike Frager
    Raj Goel
    Stefan Hornburg   
    Tim Baverstock
    William Dan Terry
    many others

and, of course, the entire Perl team without whom MiniVend could not exist.

=cut
