Interchange Guides: the Catalog Building Tutorial

Jon Jensen

Sonny Cook

This documentation is free; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

Abstract

The purpose of this document is to guide you through constructing a simple Interchange catalog from scratch. The standard catalogs that ship with Interchange are quite complex since they are ready to be used for business and highlight many of the capabilities that Interchange offers. Those catalogs may not be the right places to start the journey if you're interested in learning the basic Interchange building blocks.

Although this tutorial provides ready-to-use examples, please think about them (and ideally, retype them instead of copy-pasting to your test catalog).


Table of Contents

Before You Begin
Introduction
Interchange Installation
Test Server Hostname
Interchange Daemon and Catalogs
Minimal Catalog
Catalog Files
Link Program
Catalog Root Directory (CATROOT)
Interchange-managed Catalog Subdirectories
Catalog Registration: interchange.cfg
Catalog Configuration: catalog.cfg
Products Database
Page Templates
Organization
CATROOT/top
CATROOT/left
CATROOT/bottom
ITL: the Interchange Tag Language
Welcome Page
Tutorial Catalog in Action
Phase 1: Tutorial Files
Product Display
Product Listing
pages/flypage.html
special_pages/missing.html
Phase 2: Tutorial Files
The Shopping Basket
Order Link
pages/ord/basket.html
Phase 3: Tutorial Files
Order Checkout
pages/checkout.html
etc/profiles.order
special_pages/needfield.html
Credit Card Processing
etc/report
special_pages/receipt.html
Phase 4: Tutorial Files
Enhancing the Catalog
Basic Enhancements
Price Pictures
Catalog Variables
More Interesting Page Footer
Advanced Credit Card Expiration Date Options
Sorting the Product List
Adding a Search Box
Default Catalog Page
High-traffic Changes
Phase 5: Tutorial Files
Final Notes

Before You Begin

Introduction

Welcome to the Interchange Catalog Building Tutorial. Interchange originally started as an electronic cart and database display system, but over time it developed into a general application server. Interchange is written in Perl programming language, and it gives you the full power of Perl in Interchange pages. While being familiar with Perl (or programming languages in general) is definitely an advantage, you do not have to be a programmer to use Interchange. Interchange offers elegant and convenient, HTML-like, ITL (Interchange Tag Language) tags to access its features.

The simple Interchange catalog you will create during this tutorial should give you a feel of the basic Interchange system. The catalog should also be considered a stepping stone to a more complete and functional Interchange-enabled website. We will rely on default settings as much as possible, to accentuate ideas instead of implementation. We will use a small number of Interchange features and still create a usable website (an on-line store in our example). The resulting site will be simple but usable. The value of this tutorial is in the explanations of the general Interchange ideas and small ready to use examples to get you up to speed.

To make the most out of this tutorial, we recommend that you create the files used in this tutorial yourself, instead of copy-pasting the examples.

Writing a complete tutorial without the feedback from the audience is hard. Please jot down your notes and remarks as you digest this tutorial and e-mail authors with your comments.

Interchange Installation

For installation instructions, see Interchange Guides: Installation.

Test Server Hostname

We recommend that you always use your full system name (such as myhost.mydomain.local) instead of localhost. The HTTP State Management Mechanism (RFC 2109) specifies that cookies can only be set when the domain name contains at least two dots. Interchange does work even with client cookies disabled, but your session will be dropped every time you leave the catalog, since the session ID (which Interchange then embeds in the URL) will be lost.

If your system does not have a suitable name, and you're not going to bother yourself with establishing one, there's the /etc/hosts file you can tune for the desired effect. Simply modify:

127.0.0.1       localhost
		

to become

127.0.0.1       localhost myhost.mydomain.local mydomain.local
		

and you'll be able to use myhost.mydomain.local as your server name.

Interchange Daemon and Catalogs

To control the Interchange daemon, use the init.d script supplied with the RPM and DEB packages. For example, to start Interchange, run /etc/init.d/interchange start or /etc/rc.d/init.d/interchange start. To stop or restart Interchange, simply use stop or restart as arguments.

For more detailed information, see Q: .

Minimal Catalog

Catalog Files

This section describes the minimum of files and directories that need to be established to create a properly functioning catalog named "tutorial". Start creating the files with superuser privileges; you'll be notified when we move to regular users space.

Link Program

First of all, you need a link program. Read the link program glossary entry for an introduction and compilation instructions.

It is perfectly possible to re-use the existing link program under the same server instance. So if you already have one compiled, you only need to find it (look into your cgi-bin directory) and copy it to a new catalog's name, making sure the permissions stay intact. Run

  • cd /usr/lib/cgi-bin/ic; cp -p vlink tutorial (on Debian GNU), or

  • cd /var/www/cgi-bin; cp -p standard tutorial (on RPM-based systems).

If everything is working correctly, typing ls -l /usr/lib/cgi-bin/ic/ or ls -l /var/www/cgi-bin should roughly describe your files like this:

-rwsr-xr-x    1 interchange  interchange      7708 Dec 16 22:47 standard
-rwsr-xr-x    1 interchange  interchange      7708 Dec 16 22:47 tlink
-rwsr-xr-x    1 interchange  interchange      7708 Dec 16 22:47 tutorial
-rwsr-xr-x    1 interchange  interchange      7708 Dec 16 22:47 vlink
		

The link program enables communication with the Interchange daemon. You might notice both vlink and tlink files present; vlink uses Unix sockets to talk to the Interchange daemon (what you want most of the time), while tlink uses Inet sockets[1] and won't be used in this tutorial.

Catalog Root Directory (CATROOT)

In the Interchange (base) catalogs directory, you need to create a new subdirectory for the tutorial catalog. This is where all of your catalog-specific files will go. The directory needs to be readable, writable, and executable by the Interchange user. This will be referred to as your catalog directory[2] or CATROOT.

If you don't know where your CATROOT directory is, use any directory with write permissions (e.g. ~/catalogs/ is a good choice).

Then type the following within CATROOT:

mkdir tutorial
chown interchange.interchange tutorial
chmod 775 tutorial
		

For the rest of this tutorial, all file locations will be given relative to the tutorial catalog directory. For example, pages/ord/basket.html would be /var/lib/interchange/catalogs/tutorial/pages/ord/basket.html on Debian GNU, /var/lib/interchange/tutorial/pages/ord/basket.html on RPM-based systems, or the equivalent (such as ~/catalogs/tutorial/pages/ord/basket.html). The only exception is interchange.cfg, which is implicitly meant to be edited in /etc/interchange/ (or in the Interchange software directory ICROOT, such as /usr/local/interchange/ or ~/interchange/).

In addition, at places which require full pathnames (or other "hard-coded" values, such as usernames), Debian defaults will be used to avoid duplication and preserve the natural text flow. RPM-based systems users, please translate Debian GNU paths and other names to your system equivalents.

Interchange-managed Catalog Subdirectories

Now switch from the superuser to the Interchange user (interchange by default), by typing su -s /bin/sh - interchange. It is important to complete the rest of the steps under the "interchange" account or you'll run into permission problems later. (By the way, also make sure your umask setting is correct by running umask 022).

Move to the catalog directory (/var/lib/interchange/catalogs/tutorial/, /var/lib/interchange/tutorial/, ~/catalogs/tutorial/ or the like).

There, you need to create few catalog subdirectories that Interchange will use as it sees fit. The session/ subdirectory of the catalog root will be used to save information on visitors' browsing sessions. The etc/ and tmp/ directories are needed too, your catalog wouldn't work without them. The directories go under your catalog directory. Type mkdir session etc tmp to create them all.

For the next part, prepare your text editor of preference.

Catalog Registration: interchange.cfg

Interchange configuration is controlled by a number of directives, which are specified in two main files. Global configuration directives go to the interchange.cfg file which is common for all catalogs running on servers started from the same Interchange installation directory (ICROOT). Catalog-specific configuration directives go to catalog.cfg in the catalog directory.

The first directive we need to add is the global Catalog directive, specifying details for the new catalog. Open file /etc/interchange/catalogs.cfg and add the following:

Catalog  tutorial  /var/lib/interchange/catalogs/tutorial  /cgi-bin/ic/tutorial
		

At the end of /etc/interchange/interchange.cfg, add:

DebugFile /var/log/interchange/debug.log
ErrorFile /var/log/interchange/error.log
		

As you can intuitively see, an example of a complete (although somewhat basic) configuration directive consists of the directive name (Catalog, DebugFile, ErrorFile, ...), followed by directive-specific parameters. Any number of spaces or tabs can appear between the directives and their options. The directives are case-insensitive, but it is recommended that you stick to your choice for readability. You can insert blank lines or comment lines (lines where the first non-blank character is '#') throughout the configuration files. The order in which the lines appear is important, but it won't matter in the simple catalog we're creating.

For more information on Interchange configuration files, see configuration glossary entry.

Catalog Configuration: catalog.cfg

There are few catalog.cfg directives that Interchange expects to see in order to complete the minimum configuration. Those directives are VendURL (your catalog's base URL), SecureURL (HTTPS base URL), MailOrderTo, Database and ProductFiles. If you do not have a suitable HTTPS location for your catalog (if you're not running https, for example), simply use the same value as for VendURL. In our catalog, we will use a minimal products table[3] with only the three necessary fields. The table will be TAB delimited text file, stored in products/products.txt. In general, you can specify unlimited number of databases of any type (for unlimited purposes), but at least one must be considered a Product Files database, reflecting Interchange's e-commerce roots.

So the basic /var/lib/interchange/catalogs/tutorial/catalog.cfg config file should look like this:

VendURL       http://myhost.mydomain.local/cgi-bin/ic/tutorial
SecureURL     http://myhost.mydomain.local/cgi-bin/ic/tutorial
MailOrderTo   myname@mydomain.local

ErrorFile     /var/log/interchange/tutorial-error.log

Database      products  products.txt  TAB
ProductFiles  products

		

Products Database

The products database we mentioned, kept in products/products.txt, will serve two purposes. It will provide Interchange with the layout of the products database table and it will also provide the data. When Interchange comes around to parse the products.txt file, it will expect the first line to contain the names of the fields for the database table (the sku, description and price fields are mandatory for a products database, but you can add arbitrary other fields as you see fit). The first field in the list is expected to be the primary key (unique identifier). In most cases you are going to use the SKU (Stock Keeping Unit) as the unique identifier for each product. The simple store that we are going to build will sell some kind of tests or exams. You can choose another sample product line, but it is recommended that you keep it simple. Create the file products/products.txt, separating fields with a single TAB:

sku	description	price
4595	Nice Bio Test	275.45
2623	Stack of Econ Quizzes	1.24
0198	Really Hard Physics Test	1589.34
1299	Ubiquitous diff eq final	37.00

		

You may notice that the columns don't line up in your text editor. This is the nature of tab-delimited files. Do not try to fix these.

If you need more entries for the sample products database, you can use the dbgen Perl script to automatically create random database files for testing. The output of the script is much more meaningful if you provide it a list of words to work on (instead of just random characters) so make sure you have the /usr/share/dict/words file (in Debian, it's provided by the wenglish package), and then run perl dbgen -c 10 -r /usr/share/dict/words > products/products.txt. See the script source for available options and the complete usage syntax.

It is important to know that Interchange does not use plain-text databases (such as our products database) as-is; the performance would be poor. So instead, the data is imported and stored in a variant of the DBM database (Berkeley DB or GDBM, depending on which variant is available on your system). That's why on most Linux systems, you will see the products/products.gdbm file created once you put the catalog on-line. It is also possible to specify a different backend, such as MySQL, PostgreSQL or Oracle database; in fact, most non-trivial setups choose some SQL backend.

Whenever you use a DBM-based backend and edit the text database file, Interchange will detect the change and re-import the source file into the DBM database on next access. All aspects of this auto-import (including its deactivation) are of course controlled by config file options (but are out of scope of this tutorial).

Now that we have the majority of base files ready, we just need to create the HTML page templates.

Page Templates

Organization

Since most sites have certain aspects of the site that remain the same as the content of the pages changes, we are going to create a template that we can use for all pages. We'll divide the page into four sections:



 _____________________
|                     |
|         top         |
|                     |
|---------------------|
|      |              |
|      |              |
| left |     main     |
|      |              |
|      |              |
|---------------------|
|                     |
|        bottom       |
|_____________________|


The main section will hold the content different for each page. The top section will be used for headers, banners, menus, and so on. The left section can be used as a sidebar or navigation bar, and the bottom section can contain the copyright and contact info. The top, left, and bottom sections will remain constant throughout the site. Making a change to information in one of these sections will make that change to all pages in your site.

Now using the code displayed below, create the appropriate HTML files for each of the sections (naming the files after sections). No '.html' suffixes are needed here because those file parts are not meant to (and in fact can't) be parsed as standalone documents by Interchange (or the web server).

CATROOT/top

<html>
<head>
<title>The Interchange Test Catalog</title>
</head>

<body>
  <div align=center>
  <table width="80%" border cellpadding="15">
  <tr><td colspan="2" align="center">
    <h1> The Interchange Test Catalog </h1>
  </td></tr>

		

CATROOT/left

  <tr>
  <td align="center">
    (left)
  </td>
  <td align="center">

		

CATROOT/bottom

  </td>
  </tr>
  <tr><td colspan="2" align="center">
    (bottom)
  </td>
  </tr>
  </table>
  </div>
</body>
</html>

		

ITL: the Interchange Tag Language

We need a way to pull those template pieces we just created into the proper places to make complete, reviewable pages. This is done using ITL, the Interchange Tag Language.

[Tip]Tip

ITL uses "tags" (enclosed in [tag] brackets), much like the HTML uses tags enclosed in <tag>. The major difference, however, is in the hierarchy where the tags are parsed. ITL tags, parsed on the server side by the Interchange daemon, are expanded into the plain text and HTML which is then (over the web server) delivered to the end user and parsed there for final presentation in user's browser.

ITL is at the heart of almost all Interchange catalog pages: ITL is the way you use Interchange's functionality. The ITL tags appear between square brackets, and accept all named or all positional parameters; here's an example (make sure you understand this!):

[data table="products" column="price" key="1299"]  (named parameters, NAME=VALUE NAME=VALUE ...)

[data products price 1299]  (positional parameters, VALUE VALUE ...)
		

[Important]Important

You can't use positional parameters if the values contain whitespace. For example, [tagname "[data session mv_arg]"] is invalid; the only way to specify that is [tagname optionname="[data session mv_arg]"]. Also, if the first parameter is positional, all must be positional (and vice versa, if the first parameter is named — all must be named).

Tags can span multiple lines which helps readability when the tags have a large number of (long) options. There's a whole lot of tags available (around 200 in Interchange 5.2), but in this tutorial very few will be addressed. For a complete listing of the ITL tags, see the Tags reference.

The first tag we'll use is going to be the include tag; it reads the specified file (relative to the catalog directory — CATROOT), reparses it for any Interchange tags, and puts the final result in place of the tag. We'll demonstrate that now on the next page you'll need to create.

Welcome Page

Create a directory called pages/ in your tutorial catalog directory (the CATROOT).

Save the following text as pages/index.html. This will create a page to test that everything works so far.

[include top]
[include left]

This is where your content goes.

[include bottom]

		

Interchange pages in the pages/ directory (or elsewhere, when configured) must have the .html suffix. You can omit the suffix in both your URLs when accessing pages with a Web browser, and in the tags you call (such as page), but the files on the disk must be properly named. (In fact, the actual suffix is controlled by the HTMLsuffix config directive).

Tutorial Catalog in Action

As the superuser, first restart Interchange to make sure all changes are applied to the Interchange server and catalog.

Monitor the log files: tail -f /var/log/{interchange,apache*}/*log

Open the web browser and load the page. Your URL should be http://myhost.mydomain.local/cgi-bin/ic/tutorial/index.html or http://myhost.mydomain.local/cgi-bin/tutorial/index.html. Since the catalog.cfg only contains minimal configuration, index.html is not defined as the default page, so you can't leave it out of the URL.

Congratulations! Your basic, "phase 1" catalog is now hopefully finished and working ;)

[Note]Note

If you receive an error visiting the catalog ("Interchange server unavailable"), edit interchange.cfg and add SocketPerms 0666 to the end of file; then restart Interchange and try again.

If you get a script error ("Internal Server Error"), chances are you tried visiting the catalog without "/index.html at the end of the URL).

Phase 1: Tutorial Files

We have prepared the files from this tutorial phase for download in .tar, .tar.gz, .tar.bz2, and expanded formats. Special notes on turning the files into a working catalog are not included; that's what this document is for.

Product Display

Product Listing

Now that your sample catalog is up and running, we'll display your products on the welcome page. We will loop over all of the products in our database and produce an entry for each product in the table. Replace the line "This is where your content goes" line in pages/index.html with the following:

<table cellpadding="5">
  <tr>
    <th>Test #</th>
    <th>Description</th>
    <th>Price</th>
  </tr>

  . . .

</table>
    

Now we will use Interchange tags to fill in the rest of the table with the products database you created. The loop ITL tag pair tells Interchange to iterate over each item in the parameter list. In this case, the loop runs over the results of an Interchange search. The search parameter does a database search on the provided parameters. In this case, we're doing a very simple search that returns all of the fields for all of the entries in the products database. The parameters passed to the search tell Interchange to return all records ('ra') from the products file ('fi'). The following should take the place of the ellipsis (the "three dots") in the above code that we placed in pages/index.html:

  [loop search="ra=yes/fi=products"]

  . . .

  [/loop]
    

In the loop we just established, the individual elements of each record are accessed using the loop-field tag. The following code should replace the above ellipsis in the code we placed in pages/index.html:

  <tr>
    <td>[loop-code]</td>
    <td>[loop-field description]</td>
    <td align="right">[loop-field price]</td>
  </tr>
    

The [loop-code] tag refers to the primary key (unique identifier) for the current row of the database table in question. In this case, it will produce the same output as the [loop-field sku] tag, because the 'sku' field is the primary key ("code") for products table. In each case, the tag is replaced by the appropriate element. When put together, Interchange generates a page with your products table on it.

Your finished pages/index.html page should look like this:

[include top]
[include left]
<table cellpadding="5">
  <tr>
    <th>Test #</th>
    <th>Description</th>
    <th>Price</th>
  </tr>
  [loop search="ra=yes/fi=products"]
  <tr>
    <td>[loop-code]</td>
    <td>
      [page [loop-code]]
      [loop-field description]
      </a>
    </td>
    <td align="right">[loop-field price]</td>
  </tr>
  [/loop]
</table>
[include bottom]

		

Test it by refreshing index.html page in your browser.

pages/flypage.html

The next step is to create an individual page for each item. You could theoretically create each page manually, but that approach isn't worth considering. So to do this the right way, we need to create a special generic page called the flypage (pages/flypage.html). When a page is requested that does not exist in the pages/ directory, Interchange will check and see if the requested page matches an existing product code from the products database. If it does, it will then show the flypage and populate it with the corresponding product's data. If there's no product with that ID, the special error page special_pages/missing.html (described in the next section) will be displayed.

For example, if you request page 0198.html, Interchange first checks if that specific page exists (pages/0198.html). If one is not found, it searches the products database table for product code 0198. When found, Interchange creates product page on the fly using pages/flypage.html. When constructing the flypage, the entire product record for the requested product is available through the item-field tag (similar to the loop-field tag). To create a fly page, type the following code and save it as pages/flypage.html.

[include top]
[include left]

<h3>Test #[item-code]</h3>
<p>[item-field description] . . . [item-field price]</p>

<br/>
Return to the [page index]Index page</a>.
[include bottom]

		

special_pages/missing.html

Create the special_pages/ directory in your catalog directory (the CATROOT).

It is a good idea to display an error page when Interchange is asked for an unknown page. To create a missing page for display, type the following and save it as special_pages/missing.html.

[include top]
[include left]
<p>We're sorry, the page you requested has not been found.</p>

<p>Try finding what you need on the [page index]welcome page</a>.</p>
[include bottom]

		

The addition of this page ensures that users see your error message instead of a mysterious server error if they mistype the URL or click a broken link.

Phase 2: Tutorial Files

We have prepared the files from this tutorial phase for download in .tar, .tar.gz, .tar.bz2, and expanded formats.

The Shopping Basket

Order Link

Now that you have your products available, let's add a shopping cart so customers can purchase them. This is simply created using the order tag. The tag creates an HTML link that causes the specified item to be ordered and takes the shopper to her basket page. This is a built-in shortcut to the complete order process which uses an HTML form submission process. The parameter for the order tag is the product ID. To add these tags to the catalog, make the following change to pages/index.html:

      [loop-field description]
      </a>
    </td>
    <td align="right">[loop-field price]</td>
+    <td>[order [loop-code]]Order Now</a></td>
  </tr>
  [/loop]
    

The single line you need to add is marked by a '+'. However, do not include the '+' when adding this line. The surrounding lines are shown to give you the context. This style is called "context diff", and will be used extensively.

pages/ord/basket.html

Create the directory pages/ord/ in the tutorial catalog directory.

For the order tag, Interchange expects a default page called pages/ord/basket.html. This page displays the contents of the shopping basket and contains other shopping basket functionality.

The standard demo catalog has a full-featured shopping basket available for use, but this tutorial teaches you to build your own simple one. The shopping basket items can be accessed using a set of tags that have prefix of "item". Put the following code in the new pages/ord/basket.html file. The section that follows explains the tags used.

[include top]
[include left]

<h2>This is your shopping cart!</h2>

<table cellpadding="5">

<tr>
  <th>Qty.</th>
  <th>Description</th>
  <th>Cost</th>
  <th>Subtotal</th>
</tr>

[item-list]
<tr>
  <td align="right">[item-quantity]</td>
  <td>[item-field description]</td>
  <td align="right">[item-price]</td>
  <td align="right">[item-subtotal]</td>
</tr>
[/item-list]

<tr><td colspan="4"></td></tr>

<tr>
  <td colspan="3" align="right"><strong>Total:</strong></td>
  <td align="right">[subtotal]</td>
</tr>

</table>

<hr>

<p>
[page checkout]Purchase now</a><br>
[page index]Return to shopping</a>
</p>

[include bottom]

		

The basket items can be accessed one at a time by using the item-list tag. So we will create a table by iterating through the basket items. The text within the item-list /item-list tags is created for each item in the list.

[item-quantity] shows the quantity of the item ordered. If the same item is ordered multiple times, the quantity increases.

[item-field description] shows the description from the product database table. Any field that is not special to Interchange can be accessed from the shopping cart this way.

item-price shows the per-item price that is defined in the product database table.

item-subtotal shows the total cost of this order line. This is normally the price multiplied by the quantity, but it can also take into account other considerations, such as various kinds of price discounts.

subtotal shows the calculated shopping basket subtotal.

[page index] creates the starting HTML <a href=...> for a link to the catalog welcome page.

You also need to put a link in the index page so that shoppers can go to their shopping cart without ordering something. Modify the end of pages/index.html by adding the following lines:

  </table>
+ <hr>
+ <p align=center>[page order]View shopping cart</a></p>
  [include bottom]
    

Refresh the index page and test the shopping basket in your browser.

Phase 3: Tutorial Files

We have prepared the files from this tutorial phase for download in .tar, .tar.gz, .tar.bz2, and expanded formats.

Order Checkout

pages/checkout.html

The site can now be completed by adding the ability to check out with the shopping cart and finalize the order. To do this the customer needs to provide a shipping address (which, for the sake of this tutorial, we will assume is the same as the billing address), and payment information. We will process the order by verifying the customer's payment information and sending an email to the merchant (ourselves) detailing the order.

We first need to create a checkout page. The checkout page consists of a form that receives order information from the customer and performs a simple credit card number check. In this tutorial we will use a built-in test that only checks to see if a given credit card number could be valid. If the information is acceptable the customer will move to the next phase of the order process. If it is not, an error page will be displayed.

To create a checkout page, type the following code and save it as pages/checkout.html. The section that follows explains the code.

[include top]
[include left]
<h1>Checkout Page</h1>

<form method="post" action="[process]">
[form-session-id]
<input type="hidden" name="mv_todo" value="submit" />
<input type="hidden" name="mv_order_profile" value="order_profile" />
<input type="hidden" name="mv_cyber_mode" value="minivend_test" />

<table cellpadding="3">

<tr>
<td align="right"><b>First name:</b></td>
<td><input type="text" name="fname" value="[value fname]" /></td>
</tr>

<tr>
<td align="right"><b>Last name:</b></td>
<td><input type="text" name="lname" value="[value lname]" /></td>
</tr>

<tr>
<td align="right" rowspan="2"><b>Address:</b></td>
<td><input type="text" name="address1" value="[value address1]" /></td>
</tr>

<tr>
<td><input type="text" name="address2" value="[value address2]" /></td>
</tr>

<tr>
<td align="right"><b>City:</b></td>
<td><input type=text name="city" value="[value city]" /></td>
</tr>

<tr>
<td align="right"><b>State:</b></td>
<td><input type="text" name="state" value="[value state]" /></td>
</tr>

<tr>
<td align="right"><b>Postal code:</b></td>
<td><input type="text" name="zip" value="[value zip]" /></td>
</tr>

<tr>
<td align="right"><b>Country:</b></td>
<td><input type="text" name="country" value="[value country]" /></td>
</tr>

</table>

<p>
Note: We assume that your billing address is the same as your shipping address.
</p>

<table cellpadding="3">

<tr>
<td align="right"><b>Credit card number:</b></td>
<td><input type="text" name="mv_credit_card_number" value="" size="20" /></td>
</tr>

<tr>
<td align="right"><b>Credit card expiration date:</b></td>
<td>
Month (number from 1-12):
<input type="text" name="mv_credit_card_exp_month" value="" size="2" maxlength="2" />
<br>
Year (last two digits only):
<input type="text" name="mv_credit_card_exp_year" value="" size="2" maxlength="2" />
</td>
</tr>

</table>

<p>
<input type="submit" name="submit" value="Finalize!" />
<input type="reset" name="reset" value="Reset" />
</p>

</form>

<p>[page index]Return to shopping instead</a></p>
[include bottom]

		

The HTML form begins with a method of 'post' (which sends the form data as its own stream, as opposed to the 'get' method which encodes the data as part of the URL). The process tag creates a special URL for form processing. Interchange has a built-in form processor that is configured by submitting certain fields in the form. The Finalize button will invoke this form processor and link the user to the special_pages/receipt.html page, which is described later.

The code [form-session-id] basically just expands to a hidden form variable that specifies the session ID. It is very important to always include this in every HTML form. That way, Interchange can work properly even when the client is not accepting cookies!

You are also submitting some hidden form values that will tell Interchange how to process this form. The first value, mv_todo was set to "submit". This causes the form to be submitted for validation. The second value, mv_order_profile was set to "order_profile. This determines the name of the profile according to which the validation will be performed.

The last value, mv_cyber_mode, was set to "minivend_test". The mv_cyber_mode value determines what method will be used to charge a credit card. The value of minivend_test uses the internal test method, which calculates a simple checksum against the card to determine if it is a valid number.

When preparing an order for processing, Interchange looks for certain named fields in the form to obtain name, address, and credit card information. We are using all expected (default) field names in this form so that no translation needs to take place.

View the checkout page in your browser. The "Finalize!" link has not been enabled, but the page should display properly.

etc/profiles.order

You need to set up verification for the order form by defining an order profile for the form. An order profile determines what fields are necessary for the form to be accepted. Create an order profile by typing the following and saving it as etc/profiles.order. The section that follows explains the code used:

__NAME__ order_profile

fname=required
lname=required
address1=required
city=required
state=required
zip=required

&fatal=yes
&final=yes
&credit_card=standard keep

__END__

		

A single file can contain multiple profile definitions. First the profile is named using the <pragma>__NAME__</pragma> pragma (but this is unrelated to the __VARIABLE__ syntax seen elsewhere in Interchange). Then in the profile there is a list of the form fields that are required. The &fatal setting indicates that validation will fail if any of the requirements are not met. &final indicates that this form will complete the ordering process. This setting is helpful if you have a multi-page ordering process and you want to validate each page individually. The <pragma>__END__</pragma> pragma signals the end of this profile, after which you can begin another one.

In order to activate your order profile, add the OrderProfile directive to the catalog.cfg:

OrderProfile etc/profiles.order
		

Watch for white space in front of the <pragma>__NAME__</pragma> pragma, it can cause your profile to be ignored. Remember to reconfigure the catalog or simply restart Interchange altogether after modifying catalog.cfg or the profiles.

special_pages/needfield.html

If the submitted form lacks a required field, Interchange will display an error page. The default location is special_pages/needfield.html. Here's the code for the page:

[include top]
[include left]
<p>The following information was not given:</p>

<p><b>[error all=1 show_var=1 show_error=1 joiner='<br>']</b></p>

<p>Please go back to the [page checkout]checkout page</a>
and fill out the form properly.</p>

[include bottom]

		

The error tag is the most important tag on this page. The 'all' parameter tells the tag to iterate through all of the errors reported from the failed verification, and the 'show_var' parameter indicates that the failed variable name should be displayed. For example, if the first name was left empty, 'fname' would be shown. The 'show_error' parameter displays the actual error for the variable. The joiner parameter inserts an HTML <br> tag between each error message, so each error is displayed on its own line. In more complex configurations, the error tag can be even more expressive.

Credit Card Processing

This tutorial uses a very simple order process. To accomplish this, we use "&credit_card=standard keep" in the file etc/profiles.order. It issues two instructions to the credit card system:

The first option, standard, uses the standard built-in encryption algorithm to encrypt the credit card number and erases the unencrypted copy from memory. We are using the standard option not to encrypt the number but to run the checksum verification on the number to verify that it is a potentially correct number. We will not be checking with a real payment processor to see if it actually is a valid card number. For testing purposes, you can use the card number 4111 1111 1111 1111, which will pass the checksum test.

The second option, keep, keeps the credit card number from getting removed from memory. We want to keep the number in memory so that it is available when it is mailed to the store owner as part of the order.

If the credit card number passes and all of the required fields are present, the customer will be sent to the final page. Interchange then sends an e-mail to the store owner.

etc/report

When the customer's involvement in the order is complete, Interchange composes an email and sends it to the recipient defined in the MailOrderTo directive in catalog.cfg. The default location for this email report template is etc/report. Interchange tags can be used to fill in the body of the message.

The report should include at least the customer's name, address, and the items they ordered. The following is a simple report template; save it as etc/report:

               Name: [value fname] [value lname]
            Address: [value address1][if value address2]
                     [value address2][/if]
  City, State, etc.: [value city], [value state]  [value zip] [value country]

      Credit Card #: [cgi mv_credit_card_number]
    Expiration Date: [cgi mv_credit_card_exp_month]/[cgi mv_credit_card_exp_year]

  ************ ORDER ************
  [item-list]
  [item-quantity] x [item-description] ([item-code]), [item-price] ea.
  [/item-list]
  Subtotal: [subtotal]
     Total: [total-cost]

		

This file is in plain text format where, unlike HTML, white space is relevant. It is fairly straightforward, except that the if tag was added to only include the optional second address line if the customer filled it in.

One of the special properties of the mv_credit_card_number field is that Interchange specifically precludes the credit card number from being saved. This makes it unavailable to you in the value tag. The cgi tag is used to circumvent this important security measure in order to get the value submitted from the last form.

[Warning]Warning

Obviously it is a bad idea to send a real credit card number over an insecure channel like email. In a real configuration, you would encrypt the number securely before emailing or storing it.

special_pages/receipt.html

Once the report has been run, Interchange will finish the order process on the customer side by displaying a success screen containing a receipt. The default location for this page is special_pages/receipt.html. To create a receipt page, type the following code and save it as special_pages/receipt.html.

[include top]
[include left]
<p>Thank you for ordering stuff from us.<br>Have a nice day!</p>
<p>[page index]Return to our welcome page</a></p>
[include bottom]

		

Once the order is processed, the customer's shopping cart is emptied.

At this point you have a more-or-less functional store. Congratulations once again ;)

Phase 4: Tutorial Files

We have prepared the files from this tutorial phase for download in .tar, .tar.gz, .tar.bz2, and expanded formats.

Enhancing the Catalog

Basic Enhancements

Now that you have a working catalog, you can go back and add improvements and test them incrementally. This section walks you through several, and then suggests even more enhancements you can attempt to do on your own (the exercises for the readers are fantastic, aren't they :).

Price Pictures

You may have noticed that the product prices aren't formatted as prices usually are. The way to correct this is with an Interchange feature called price pictures.

There are several properties to price pictures: the currency symbol, the thousands separator, the decimal point, the number of digits to show behind the decimal, and so on. Most Unix systems have U.S. currency and the English language as the default locale, which is called en_US. The only thing you need to do on such a system is specify the currency symbol, which, in this case, is the dollar sign. To do this, add the following line to your catalog.cfg file:

Locale en_US currency_symbol $
		

Restart Interchange and view your catalog. You will notice little has changed on the welcome page or the flypages, but in the shopping cart (pages/ord/basket.html) all your prices should be formatted as U.S. dollars ("1347.3" has become "$1,347.30"). Why the currency is only displayed on the basket page is easy to understand; we use the item-price tag there. That tag is equivalent to [item-field price] used elsewhere, but it has that extra logic associated with it that automatically displays the currency format. To use item-price without the auto-format, you'd have to change the [item-price] tag to [item-price noformat].

But that's probably not what you want to do. You're probably more interested in formatting all the other prices (such as those on the Welcome page) to a proper currency-dependent display. To do that, you could obviously replace [item-field price] with [item-price], but we'll take on more general approach here. Simply use the currency tag for all price values. Make the following change to pages/index.html:

  [loop search="ra=yes/fi=products"]
  <tr>
    <td>[loop-code]</td>
    <td>
      <a href="[loop-code].html">
      [loop-field description]
      </a>
    </td>
-    <td align="right">[loop-field price]</td>
+    <td align="right">[currency][loop-field price][/currency]</td>
    <td>[order [loop-code]]Order Now</a></td>
  </tr>
  [/loop]
    

[Note]Note

The line that begins with '-' should be deleted. Do not type the '-'. The next line, that starts with '+', replaces it. (It's the context diff format we mentioned, remember?)

A similar change to the [item-field price] tag in the pages/flypage.html page will fix that currency display. View the page in your browser. All your prices should be formatted for U.S. currency.

If your prices are not being formatted correctly, your default system locale may be set up differently or your en_US locale settings may be wrong. There are a few other catalog.cfg directives you can use to correct the situation:

Locale en_US p_cs_precedes 1
		

Makes the currency symbol precede the currency value. A '0' setting makes the symbol come after the currency value.

Locale en_US mon_thousands_sep ,
		

Sets your thousands separator to a comma. It can be set to any value.

Locale en_US mon_decimal_point .
		

Sets your decimal separator to a comma. Many countries use a comma instead of a period to separate the integer from the decimal part.

[Note]Note

Consult the Interchange documentation and your operating system manual for more information on locale settings.

Catalog Variables

Interchange provides a very useful feature that has not been discussed yet called catalog variables. It provides a way for you to set a variable to a certain value in the catalog.cfg file and then use it anywhere in your catalog pages. The Variable directive allows an Interchange catalog variable to be created with the name coming from the first parameter and the value from the rest of the line, like this:

Variable SOMENAME whatever value you want
		

To access that variable in your pages, type the token __SOMENAME__. Notice that there are two underscore characters before the variable name and two after it, and that in place of the word SOMENAME you would put the actual name of the variable. The first thing Interchange does on a page is to replace the token with the variable's value. The variable value can also include Interchange tags to be parsed.

This was an example of a catalog variable. There are also global variables which are defined in the same way (but in the interchange.cfg file), and are accessed using the @@SOMENAME@@ token. A convenient, @_SOMENAME_@ syntax is also valid, and first checks the existence of a variable in the local catalog, falling back to the global value if no catalog-specific value has been set.

More Interesting Page Footer

You can put a contact email address at the bottom of each page in case your customers want to contact you. You could just add it to the footer, but by putting it into a variable you can use it in contact pages as well. This allows you to easily change the variable information and have that change reflected in all instances of that variable. The following is an example of how to set a catalog variable in catalog.cfg:

Variable CONTACT_EMAIL myname.surname@mydomain.local
		

Now make the following change to your template file bottom:

  </td>
  </tr>
  <tr><td colspan="2" align="center">
-    (bottom)
+    <a href="mailto:__CONTACT_EMAIL__">Contact us</a> if you have any questions.
  </td>
  </tr>
  </table>
  </div>
</body>
</html>
    

Be sure to restart Interchange (or reconfig the catalog at least) before reloading the page in your browser, since you made a change to catalog.cfg.

Let's add another variable to your catalog. This variable demonstrates how an Interchange tag can be included in the variable. This Interchange tag returns the current date in a standard format. Add the following to catalog.cfg:

Variable DISPLAYDATE [time]%A, %B %d, %Y[/time]
		

Now add the following to the left template piece:

  <tr>
  <td align="center">
-    (left)
+    __DISPLAYDATE__
  </td>
  <td align="center">
    

Restart Interchange and view the page.

Advanced Credit Card Expiration Date Options

To reduce the possibility of human error at checkout time, most on-line stores use a pull-down option menu to list the months and the years for the credit card expiration date, instead of having the user to type the numbers by hand. The menu also lets you avoid explaining whether the user should enter a 2- or 4-digit year.

Make the following change to your pages/checkout.html page. The section that follows explains the code. Read the explanation section below before typing the code to be sure you know where tabs should be used instead of spaces and where to watch out for `back-ticks`.

  <tr>
  <td align=right><b>Credit card expiration date:</b></td>
  <td>
- Month (number from 1-12):
- <input type=text name=mv_credit_card_exp_month value="" size=2 maxlength=2>
- <br>
- Year (last two digits only):
- <input type=text name=mv_credit_card_exp_year value="" size=2 maxlength=2>
+
+ Month:
+ <select name=mv_credit_card_exp_month>
+ [loop
+    lr=1
+    option=mv_credit_card_exp_month
+    list="
+ 1     01 - January
+ 2     02 - February
+ 3     03 - March
+ 4     04 - April
+ 5     05 - May
+ 6     06 - June
+ 7     07 - July
+ 8     08 - August
+ 9     09 - September
+ 10    10 - October
+ 11    11 - November
+ 12    12 - December"]
+ <option value="[loop-code]">[loop-pos 1]
+ [/loop]
+ </select>
+
+ Year:
+ <select name=mv_credit_card_exp_year>
+ [comment]
+    This should always return the current year as the first, then
+    seven more years.
+ [/comment]
+ [loop option=mv_credit_card_exp_year lr=1 list=`
+   my $year = $Tag->time( '', { format => '%Y' }, '%Y' );
+   my $out = '';
+   for ($year .. $year + 7) {
+     /\d\d(\d\d)/;
+     $last_two = $1;
+     $out .= "$last_two\t$_\n";
+   }
+   return $out;
+ `]
+   <option value="[loop-code]">[loop-pos 1]
+ [/loop]
+ </select>
+
  </td>
  </tr>

  </table>
    

In the first set of <select> </select> HTML tags a list is generated of the months to choose from. This is accomplished by using a loop tag. In this case we are looping over an explicit list. The list is provided in the list parameter. Use caution when typing this, as it is sensitive to formatting (which may not be reflected in this document). Make sure that the numbers are the first characters on each new line and that the single tab separates them from the rest of the line text. Since the columns in this list are not named, the first element can be accessed using [loop-code] or [loop-pos 0] with subsequent elements being accessed by [loop-pos N] where N is the number of the column you want. Notice that the elements are zero-indexed. Each time through this loop Interchange generates a select <option> with a number as the value and the name of the month as the text for the select menu.

For the next set of <select> </select> tags embedded Perl is used to generate the list which is iterated over. Perl code can be embedded in Interchange pages in order to extend the abilities of the system. Make sure you type back-ticks (grave accents) after "list=" and before the closing bracket (and not apostrophes). This code generates an entry for seven years in addition to the current year. It is not necessary at this point for you to understand this Perl code.

Sorting the Product List

The products listed on your welcome page are shown in the same order that you entered them into products/products.txt. As you add more products, you will want this list to show up in a predictable order. To do this, you need to change the search parameters in pages/index.html, which were originally:

  [loop search="
    ra=yes
    fi=products
  "]
    

You will recall that 'ra' stands for 'return all' and 'fi' stands for file. Let's add the search parameter 'tf', which specifies the sort field. You can specify the field either by name or by number (starting with 0), with names and order as given in the first line of products/products.txt). Make the following change in pages/index.html:

  [loop search="
    ra=yes
    fi=products
    tf=price
  "]
    

Refresh your browser. The default ordering is done on a character-by-character basis, but we were looking to do a numeric sort. For this you need to set 'to', the sort order, to 'n', for numeric:

  [loop search="
    ra=yes
    fi=products
    tf=price
    to=n
  "]
    

Refresh your browser. Now try reversing the sort order by adding 'r' to the 'to' setting:

  [loop search="
    ra=yes
    fi=products
    tf=2
    to=nr
  "]
    

You'll notice that it worked equally well to specify the sort field by number instead of name. You could also do a reverse alphabetical sort by description:

  [loop search="
    ra=yes
    fi=products
    tf=1
    to=r
  "]
    

Now let's try narrowing the search down a bit. Instead of returning all, we'll give 'se', the search parameter, and and use 'su', which allows substring matches. To search only for products that contain substring "test" in any of their fields, and sort the results by description, type:

  [loop search="
    se=test
    su=yes
    fi=products
    tf=description
  "]
    

Which seems like something that would be better done in a search box for your store visitors. So we'll implement a search box.

But before moving on, just change this search back to the simple list, sorted by description:

  [loop search="ra=yes/fi=products/tf=description"]
    

Adding a Search Box

Your customers might appreciate the ability to search for a test by SKU or part of the test description. To do this, you need to add a search box to the left portion of the page layout. Make the following change to the file left:

  <tr>
  <td align="center">
-    __DISPLAYDATE__
+ <form action="[area search]" method="post">
+ [form-session-id]
+ Search:<br>
+ [set testname]
+ su=yes
+ fi=products
+ sf=sk
+ sf=description
+ [/set]
+ <input type="hidden" name="mv_profile" value="testname">
+ <input type="text" name="mv_searchspec" size="15" value="">
+ </form>
+ <hr>
+ __DISPLAYDATE__
  </td>
  <td align="center">
    

This is a simple HTML form with a single input box for text. The action goes to a special Interchange processor called 'search' that will perform the search and pass the results to a page called pages/results.html (that has not been created yet). The search will be case-insensitive, but will only match complete words, not substrings.

The [set testname]...[/set] tag sets an Interchange scratch variable that will be (in this case, of course) used as a predefined search profile. We specify all the search parameters except the one the user will enter, mv_searchspec (the long name for 'se'). We then tell Interchange we want to use this search profile in a hidden form variable named mv_profile.

The search box will now appear on all catalog pages, but you still need to create the search results page. To create the search results page, type the following code and save it as pages/results.html.

[include top]
[include left]

<h3>Search Results</h3>

[search-region]

  [on-match]
  <table cellpadding="5">
  <tr>
  <th>Test #</th>
  <th>Description</th>
  <th>Price</th>
  </tr>
  [/on-match]

  [search-list]
  <tr>
  <td>[item-code]</td>
  <td><a href="[item-code].html">[item-field description]</a></td>
  <td align="right">[item-field price]</td>
  <td>[order [item-code]]order now</a></td>
  </tr>
  [/search-list]

  [on-match]
  </table>
  [/on-match]
  [no-match]
  <p>Sorry, no matches were found for '[cgi mv_searchspec]'.</p>
  [/no-match]
[/search-region]

<hr/>
<p align="center">[page index]Return to welcome page</a></p>
<p align="center">[page order]View shopping cart</a></p>

[include bottom]

		

The search results will be contained in the search-region /search-region tags. The text in the on-match /on-match container will be displayed only if matches were found for the search. The text in the no-match /no-match container will be displayed only if no matches were found. The search-list /search-list container functions just like loop /loop, iterating over its contents for each item in the search results list.

Default Catalog Page

As you know, a standard Interchange catalog page URL looks like http://myhost.mydomain.local/cgi-bin/ic/tutorial/index.html.

But what happens if you leave off the page name, as people often do when typing URLs in by hand? Visit http://myhost.mydomain.local/cgi-bin/ic/tutorial and you'll get a Page not found error. This is because Interchange is looking for special_pages/catalog.html which we didn't create. It would seem useful to "redirect" those requests to an actual existing page, most probably your catalog entry page - pages/index.html. Fortunately, this is easily achieved with the following catalog.cfg directives[4]:

SpecialPage catalog index
DirectoryIndex index
		

Restart Interchange and try the above URL again. If you want to make the welcome page something other than pages/index.html, modify the 'index' part of the directives appropriately.

High-traffic Changes

During this tutorial you have created catalog pages that use the include tag to include template pieces in the pages. This has worked well, but there are a few drawbacks. First, if you want to rename any of the template piece files or move them out of the main catalog directory and into their own subdirectory, you would have to update the include tag on every page. To avoid this, you can create catalog variables set to the include tags. Add these lines to your catalog.cfg file:

Variable TOP    [include top]
Variable LEFT   [include left]
Variable BOTTOM [include bottom]
		

Now change every instance of [include top] to __TOP__, doing the same for each include tag. At this point, you might not want to do a search-and-replace[5] on all the .html files you just created, but keep this capability in mind for the next catalog you work on.

If you made all of the replacements and then renamed and moved your top file elsewhere, you would only have to make a single change for each region in catalog.cfg to get your pages up to date:

Variable TOP    [include templates/main-top]
		

Every time a catalog page is viewed, each file in an include tag must be loaded from disk. In a test situation, this takes no noticeable amount of time. But on a busy Interchange server, this can slow your system.

You can switch to a high-traffic mode that doesn't require each template piece to be read from disk every time the page is loaded. Instead, all of the pieces are read into variables once when Interchange is started and they remain in memory until you restart Interchange, or reconfigure the catalog. On very busy Interchange catalogs, this can increase your speed noticeably. The (only) drawback is that you need to restart Interchange (or reconfigure the catalog) every time you make a change to the template pieces. You can set up those high-traffic templates by changing the Variable directives in catalog.cfg as follows:

Variable TOP    <top
Variable LEFT   <left
Variable BOTTOM <bottom
    

Phase 5: Tutorial Files

We have prepared the files from this tutorial phase for download in .tar, .tar.gz, .tar.bz2, and expanded formats.

Final Notes

First and foremost - congratulations for completing this tutorial (and scrolling down the page to see this message doesn't count!).

Every good tutorial includes a set of exercises for the readers, and we won't make an exception here. If you don't know any more Interchange than we presented in this tutorial, the following tasks might seem too difficult, but you can take a look at the excellent collection of new Interchange documentation :).

  • Send the customer a receipt by email

  • Allow customer to specify item quantities

  • Generate a unique order number for each order

  • Store each order in a database

  • Interface with GnuPG or PGP to encrypt credit card numbers in email reports

  • Organize your products into categories and group lists by category



[1] Unix sockets are a way for programs on the same machine to communicate. It is not possible to connect to an Unix socket remotely (this is interprocess communication, mind you, and it doesn't mean you can't browse the website from a different machine). When you need remote connection, you then have to use Inet sockets.

[2] Please note the difference between Interchange catalogs directory which holds all of the catalogs, and the catalog directory which, always in a context, designates one of the subdirectories.

[3] Keep in mind that Interchange uses both terms "database" and "table" to identify the same thing - a table. This terminology is a historic leftover from the time when Interchange only used a single table.

[4] We need two directives; the first one, SpecialPage, only handles the catalog entry point (the cgi-bin/ic/tutorial/ case). The other, somewhat Apache-like DirectoryIndex directive, sets the index page for catalog subdirectories (say, cgi-bin/ic/tutorial/mydir/), but unlike Apache it only takes one argument.

[5] If you're adventurous enough, try this Perl one-liner on the command line for automatic replace: cd /var/lib/interchange/catalogs/tutorial; perl -pi -e 's/\[include (\S+)\]/"__".uc($1)."__"/ge' `find . -name '*.html'`

DocBook!Interchange!