Skip navigation

STANFORD UNIVERSITY

INFORMATION TECHNOLOGY SERVICES

All About Formage

Formage is a general-purpose form-processing script so that the forms in your Leland pages can do interesting things. You can have them send you mail, write to a file, and/or send a reply to the browser. It's somewhat like its predecessor and inspiration>Mailmerge, and it's almost but not quite French for "cheese."

You tell Formage what you want it to do in a template file. The template file below is for a simple example that just formats a message back to the browser.

<INPUT_FORM>
FORMAT=MyForm
</INPUT_FORM>

<ACTION>
OUTPUT=reply
FORMAT=MyReply
</ACTION>

<FORMAT NAME=MyForm>
<TITLE>Word Survey</TITLE>
What's your favorite word?
<INPUT TYPE="text" NAME="word">
<INPUT TYPE="submit" VALUE="Tell me">
</FORMAT>

<FORMAT NAME=MyReply>
<TITLE>Word Survey</TITLE>
At %DATE%, you decided that <B>@word@</B> is your favorite word.
</FORMAT>

The example above is simple for the sake of brevity. You can do a lot with Formage. But you're not going to be able to do everything. Formage is not a programming language. We're doing our best to keep it simple, flexible, powerful, and secure, and giving it features that fit those requirements. Feel free to ask us if you can do such-and-such a thing with it. First play with it a bunch. If what you're trying to do isn't possible with Formage, then you may need to run a different script (perhaps even write it) on a different server.

When you see the word "user" below, that means the person filling out your form. You are what we call the "provider". We are what we call "us".

Features

With Formage you can:

  • format text with variables from your form and your template
  • send that text as mail, write it to a file, or send it to the browser
  • do any combination of the above when a form is submitted
  • format the text for each action differently
  • perform input validation (edit checking)
  • set variables conditionally
  • keep a logfile, which saves a record of each submission
  • limit submissions based on total number, or on duplicates
  • use the logfile as input, with simple selection and sorting
  • replace a file, append to it or prepend to it
  • create a unique new file with a sequence number and write to that
  • have Formage maintain a unique key for each record in the logfile
  • customize error messages
  • handle file uploads from the browser

This manual

The documentation below is manual-style, which means it covers pretty much everything there is to know about Formage. That may be a lot more than you need to know right now, so in the interest of speeding you on your way, here are some shortcuts:

Contents

How to invoke Formage

Each form you have will be dealt with according to a file of yours that tells Formage what to do. For lack of imagination, we call this a template file. To call Formage, just provide the URL of your template file. That's a bit different from how many of the leland web scripts (including Mailmerge) are called, which is by calling the script and passing the location of a file in the URL. All you have to do to make a file into a Formage template file is give it the extension .fft. (We're not sure what that stands for - maybe "feisty form translator" or "fast formy transform".) Whenever such a file is requested, the server will know to use Formage to deal with it.

For example, the HTML to provide a link to this >example is

<A HREF="/group/idg/leland/formage/templates/example1.fft">example</A>

You can either place your input form (what the user fills out) in your Formage template, or you can keep it in a separate file, and call Formage from there.


Comments

Comments have a # in the first column, so

# This is a comment.

If your template is at all complicated, using comments is a good way to remind yourself what you were thinking when you wrote it. The only place where a comment isn't ignored is within a FORMAT - everything in there is taken literally.


Variables

There are two main classes of Formage variables, user and system. Basically, user variables are yours, and system variables are Formage's. The major differences are:

  • Your form fields become user variables.
  • You can make up all the user variables you want, but you can only use the system variables that Formage knows about. They're used mostly to control its behavior.
  • Only user variables are written by default to the logfile (if there is one).

Variables have no default values. If neither you nor the user gives a variable a value, it will have nothing in it.

User variables

User variables are always surrounded by @ signs. They can contain letters, numbers, and underscores. For example: @FirstName@, @zip_code@, @option1@, and @TYPE@. Variable names are case-sensitive, so @name@ is different from @NAME@, which is different from @Name@. Variables have two sources:

  1. Your form variables. For example, a text input field that is named "address" in your form becomes a Formage variable called @address@. Its value is whatever the user typed into it.

  2. Your template file. You can just make them up and give them values. For example, you could put the following line in your template file: @my_hair@ = unkempt

Setting nested variables

You can set a variable whose name comes from another variable:

@@color@@ = CHECKED

If @color@ is "red", then @red@ will be set to "CHECKED". One situation where this is useful is if you're providing a form to update records and you want to show the current value of a checkbox or menu variable:

<FILTER NAME=Test>
@color@ =~ /./ => @@color@@ = CHECKED
</FILTER>

<FORMAT NAME=Test FILTER=Test>
red: <INPUT TYPE="checkbox" NAME="color" VALUE="red" @red@><BR>
blue: <INPUT TYPE="checkbox" NAME="color" VALUE="blue" @blue@><BR>
green: <INPUT TYPE="checkbox" NAME="color" VALUE="green" @green@><BR>
...
</FORMAT>

If the values have spaces in them, it won't work since variable names can't have spaces in them.

System variables

System variables are always surrounded by % signs. They are used to control the behavior of Formage and as a way for Formage to provide you with information. A list of all the Formage system variables is below. The middle column (Source) tells you whether it's something you're telling Formage (P for provider) or something Formage is telling you (F for Formage). Those marked with F* are set by Formage and cannot be changed. Those marked with P* can be set to a non-literal value (one with variables in it) only if the user is authenticated.

Only brief descriptions are given here - these variables are documented more fully elsewhere. The toggles are off by default. To turn one on, just set it equal to 1.

VariableSourceDescription
%ADMIN_EMAIL%PWho to contact if something goes awry. Usually it's your email address.
%DATE%F*The current date and time, e.g. Wed Jul 2 14:36:27 1997
%DATE_NUMERIC%F*Date/time in a numeric format suitable for sorting, e.g. 19970702143627
%DATE_UNIX%F*Date/time in Unix format (seconds since beginning of 1970), e.g. 867879387
%ERROR%F Error message text.
%ERROR_FORMAT%P Name of format to use for custom error message.
%ESCAPE_HTML%P(toggle) Escape user input for safety.
%GOT_INPUT%FSet to 1 if there was input. This is how you can tell a GET of your template from a POST if you should ever need to (which you probably won't).
%LAST_ACTION%P(toggle) Set to 1 inside an ACTION block to short-circuit further processing.
%LOGFILE%PPath of your logfile.
%LOGFILE_CURRENT%FNumber of the current entry from the logfile.
%LOGFILE_ENTRIES%FTotal number of entries selected from the logfile.
%LOGFILE_ENTRY_NUM%F*The key of the current logfile record.
%LOGFILE_ENTRY_NUM_KEY%P(toggle) Set to 1 to have Formage give each logfile record a key.
%LOGFILE_FORMVARS_ONLY%P(toggle) Set to 1 to have Formage write only form variables to the logfile.
%LOGFILE_NOEMPTIES%P(toggle) Don't write empty variables to the logfile.
%LOGFILE_SYSVARS%PWhich predefined system variables you want written to the logfile.
%LOGFILE_USERVARS%PAn explicit list of which user variables you want written to the logfile.
%LOGFILE_VARS%PA synonym for %LOGFILE_SYSVARS% for backward compatibility.
%LOGFILE_WRITE%PWhether to write the current entry to the logfile.
%MAIL_AGENT%F*Used to set a X-mail-agent: mail header.
%MAIL_CC%P*Used to set a Cc: mail header.
%MAIL_CONTENT_TYPE%PUsed to set a Content-type: mail header. Used by MIME-capable mail readers.
%MAIL_FROM%F*The value of the From: mail header that Formage sets.
%MAIL_HEADER%F*The set of mail headers that Formage sets.
%MAIL_REPLY_TO%PUsed to set a Reply-to: mail header.
%MAIL_SUBJECT%PUsed to set a Subject: mail header.
%MAIL_TO%P*The value of the To: mail header.
%MULTIVALUE%PHow to join form fields that have multiple values (e.g. multiple selection lists).
%PATH_INFO%F*Any extra stuff tacked onto the end of the template URL.
%QUERY_STRING%F*Any stuff that follows a question mark after the template URL.
%REFERER%F*The URL of the referring page. Usually your template file.
%REMOTE_HOST%F*The name of the machine the user's browser is running on.
%REMOTE_USER%F*The name of the user. It's available only if your template file is protected by Stanford's webauth" or by basic authentication, so that the user had to provide a username and password at some point to get to it.
%SEQ%F*The five-digit sequence number generated for the file_unique: method.
%SHOW_FIRST_ERROR%P(toggle) Show only the first input error, rather than all of them.
%SU_AUTH_DIRMAIL%F*The mail address of a Stanford-authenticated user.
%SU_AUTH_DIRNAME%F*The full name of a Stanford-authenticated user. This and %SU_AUTH_DIRMAIL% are not guaranteed to be unique. They may not always be present during an authenticated access since they require a successful call to the directory lookup service.
%SU_AUTH_USER%F*The SUNet ID (Leland username) of a Stanford-authenticated user. Guaranteed unique, and always available on a webauth-authenticated access.
%SUPPRESS_WARNINGS%PDon't append warnings to text.
%TEMPLATE%F*The URL of your template file.
%UPLOAD_FILE_MAX_SIZE%PThe maximum size of a file (in kilobytes) that a user is allowed to upload.
%USER_AGENT%F*The browser software being used, Netscape for example.
%VALIDATE_INPUT%PFilter to apply to input (edit checking and translation).
%VERSION%F*Current version number of Formage.
%WORDWRAP%PLine length for wrapping text.

Blocks

Each block has HTMLish opening and closing tags. The main types of blocks are INPUT_ FORM, FORMAT, FILTER, and ACTION. Action blocks are handled in the order they appear. The order of the other blocks doesn't matter.

A single template handles both displaying your form (the "front end") and processing it (the "back end"). Formage knows when to do one and when to do the other. For you webheads, GET = front end = input form, and POST = back end = actions.

The input form

The INPUT_FORM is actually just a special case of the ACTION block that's used to display your input form. All it needs to do is point at a FORMAT that contains the HTML for the form. You can do anything in your input form that you can do in an action.

It's up to you whether to include the <FORM> and </FORM> tags; if you leave them out, Formage will surround your format with them. If you include them, make sure to include METHOD="POST". You'll also need to provide an ACTION attribute if you want the form to post somewhere else, for example to a different template.

Here's a brief example:

<INPUT_FORM>
FORMAT=MyForm
</INPUT_FORM>

Told you it was brief.

You're free to place your input form in a separate file (which will probably just be a regular HTML file) and have it call Formage by pointing the ACTION attribute of its <FORM> tag at your Formage template.

Advanced use: You can use Formage variables in your input form if you like. Since no one has typed anything in yet, the only ones normally available are the ones that Formage sets for you (%DATE%, etc.), and the ones you make up in your template. There is a way to pass it your own variables, by putting them in the query string of a link to your input form. You might want to do this if you'd like to use a single template for several slightly different uses. Say you've got a sports survey for five different sports. You could pass the sport in the URL to create a slightly customized input form, and in the template you could make the name of the output file reflect the sport:

<A HREF="/~user/survey.fft?sport=football">football survey</A>

You'd then have a variable @sport@ with the value "football" that you could use in your input form and/or in the name of the file to write to. That way you don't have to make a different template for each sport you want to handle.

Note: If a variable has a value from your template file and in the query string, the latter takes precedence. That way you can give them defaults in your template.

The format

A format is a chunk of text which may have Formage variables in it. Formage goes through your format and each time it sees a variable (for example, @zip_code@ or %DATE%), it replaces it with that variable's value. Those values come from the user filling out your form, your template file, and/or Formage itself (see the description of variables above). Each format has to have a name so you can refer to it when you want to use it. The name is made up of letters, numbers, underscores, and dashes.

Here's the form for the INPUT_FORM block above:

<FORMAT NAME=MyForm>
<TITLE>Word Survey</TITLE>
What's your favorite word?
<INPUT TYPE="text" NAME="word">
<INPUT TYPE="submit" VALUE="Tell me">
</FORMAT>

And the form for the reply to the user (see below):

<FORMAT NAME=MyReply>
At %DATE%, you decided that <B>@word@</B> is your favorite word.
</FORMAT>

The action block

This is where it all happens. It's how you tell Formage "I want to take the input, make it look like this, then put it in this file and send it to that address". Your template file will have at least one action block, and it can have as many as you like. At the very least, you'll want output to go to the browser so the user sees something interesting when they're done. If you forget to do this, they'll get a lame default response.

The example above, brought to its modest fruition:

<ACTION>
OUTPUT=reply
FORMAT=MyReply
</ACTION>

Each action needs to know two things: where the output is going, and what it should look like. OUTPUT tells Formage what to do with the output. It can be set to any of the methods described below. FORMAT tells Formage how to format the output. It should be set to the name of a format you've named and defined in your template. There are three possibilities for where the output goes: send it to the browser, send it as an email message, or write it to a file in one of several ways.

You can have as many action blocks as you like. Most of the time you'll have two or three, one of which will be sending output back to the user (using the "reply" method). The actions will be performed in the order they appear in the template file.

For the sake of completeness, here's all the stuff an ACTION block can have:

<ACTION FILTER = [filter]>
OUTPUT = [output method]
FORMAT = [format]
LOGFILE_SELECT = [filter]
LOGFILE_ORDER = [sort method]
@some_user_var@ = some value
%some_system_var% = some value
</ACTION>

Giving an action a FILTER lets you perform it conditionally - if the filter fails, the action will not be performed. You can also set and change variables in the filter. LOGFILE_SELECT lets you use a subset of the logfile, and LOGFILE_ORDER lets you sort it. You can also set variables in the action block - they will have those values only while the action is being carried out (see the section on scoping).


Output methods

Printing to the screen

OUTPUT=reply

This one's really simple, and there's not much to say about it. If you don't provide a "reply" action, the user will see a page that says their form has been processed. Not real exciting. If you provide more than one "reply" action, output from all of them will go to the browser.

Sending mail

OUTPUT=mailto:youraddress@leland.stanford.edu

Just give the address you want the output sent to (typically yours). It has to be a Stanford address*. You can give multiple addresses by separating them with commas. You're not allowed to use variables in the address* - you have to tell Formage exactly where at Stanford (stanford.edu) the mail should go.

Warning #1: If the form submissions are at all important to you, you should also be writing them to a file. Mail does not always get delivered, for any of a dozen reasons: nonexistent user, bad forwarding address, mailer problems, etc. Formage hands the output to the mail system and is done with it - if there are problems down the road, Formage will have no idea. And the mail system doesn't know what to do with the bounced message - it was sent by the webserver, so there's no one to return it to. It's a problem. You get the picture.

Warning #2: Make sure you test your form! Go fill it out, submit it, and see if you get mail. You'd be surprised how many people go to the trouble of setting it up with a mail address that doesn't work.

Also, any address you set in %MAIL_CC% must be a Stanford address*.

Mail headers

Formage sends mail headers that look like this:

X-formage-host: dahlia.stanford.edu
X-mail-agent: Formage form from /~someuser/forms/test.fft
From: Anonymous Web User <wwwd@www.Stanford.EDU>
Reply-to: user@leland.stanford.edu
To: provider@leland.stanford.edu
Cc: someone@leland.stnford.edu
Subject: testing
Content-type: text/html

Here are all the mail headers, when they're sent, who can set them, and what they are.

HeaderWhenSet byContent
X-formage-host:AlwaysFThe user's machine (%REMOTE_HOST%)
X-mail-agent:AlwaysFIdentifies sender as Formage
From:AlwaysF"Anonymous", or an authenticated user
To:AlwaysPDestination address(es)
Cc:SometimesPCarbon-copy address(es)
Reply-to:SometimesPReply address
Subject:SometimesPSubject of the message
Content-type:SometimesPMIME type of mail message

Some of these, as you may have guessed, Formage sets and you (and the user) can't mess with:

  • X-formage-host: is the name of the machine the user's browser is running on
  • X-mail-agent: will tell you what template sent you this piece of mail
  • From: is set to "Anonymous Web User <wwwd@leland.Stanford.EDU>", which means that you got the mail from the Leland webserver*.

You (and the user, with your help) can set:

  • Reply-to: when you reply to the note, your mailer will send your reply to this address. To use it, just copy a form variable to it: %MAIL_REPLY_TO% = @user_address@
  • Subject: the subject of the mail message. Set it to whatever you want, a bit of text or a form variable: %MAIL_SUBJECT% = Survey Response
  • Content-type: Advanced use. Fancy mail programs can look at the MIME type of a mail message and handle it in a special way. For example, if you have in your template "%MAIL_CONTENT_TYPE% = text/html", the mail program that gets the message may well pass it on to a browser.

You alone can set:

  • To: is set based on your mailto: line, or %MAIL_TO%.
  • Cc: contains additional "carbon copy" addresses to send the message to:

    %MAIL_CC% = someone@stanford.edu

If you want the message to go to one of several Stanford addresses depending on user input, you can do that by using a filter (see below) which conditionally sets the mail variable. The same rules apply as did before: it can't have variables in it or be outside Stanford unless the user is authenticated.

Authenticated users

One of the reasons for the restrictions on mailing is that we want to be sure that a Stanford user is on at least one end of the communication. That can now be done through Stanford's web authentication setup. If you protect your template directory with webauth, the user will need to have provided their Kerberos (Leland) username and password at some point. If the user has been Stanford-authenticated in this manner, then:

  1. You can mail to off-campus addresses.
  2. You can use variables in your mailto: and %MAIL_CC% address lines.
  3. The From: header will be set to the user's Stanford mail address. It's taken from the @SU_AUTH_DIRMAIL@ variable, which got it from the Stanford directory.

For some applications the authenticated user might be you, for example if you're approving submissions that only then get appended to a file.

Saving to a file

OUTPUT=file_prepend:/~yourname/data/file.html
OUTPUT=file_append:/afs/ir/users/y/o/yourname/WWW/data/file.html
OUTPUT=file_rewrite:/~yourname/pages/file.html
OUTPUT=file_unique:/afs/ir/users/y/o/yourname/web/file.%SEQ%.html

Writing form submissions to a file has many useful possibilities - you can maintain a guestbook, run a survey, take registrations, set up a file to be read into a database later, and lots of other things.

The webserver must have write permission in the directory your file is in. It need not be in your web space, unless of course it's a web page you want people to see. To give the webserver permission to write to it, use the following command:

% fs setacl [dir] system:www-servers write

You can give a file path in one of three ways:

  1. full AFS path
  2. URL path (the part that would follow http://www.stanford.edu)
  3. relative to the directory your template is in

The first two paths above refer to the same file. You can't use .. to move up a directory in a path.

file_prepend will add to the beginning of the file. The file will be created if it doesn't already exist.

file_append will add to the end of the file. The file will be created if it doesn't already exist.

file_rewrite will replace the file's contents.

file_unique will create a new file and give it a five-digit sequence number. The variable %SEQ% will be replaced by the next sequence number (Formage figures it out by looking in the directory). You can put %SEQ% anywhere in your file path. That variable is also available if you want to use it elsewhere, but it only gets set if you're using file_unique. For example, the first submission using the line above would write to the file

/~yourname/data/file.00001.html

Many interesting, nifty, and tricky things can be done by writing to a file. Remember, you can write to any file on your AFS volume and you can dictate the format. That means that the file you write to can be an input form, another Formage template, or even a Formage logfile (using the built-in format called "FormageLogfileFormat", described below). Just what you can do with all this is left as an exercise because thinking about it hurts our heads.

Updating/rewriting the logfile

OUTPUT=logfile_update:/afs/ir/users/y/o/yourname/data/form.log OUTPUT=logfile_rewrite:%LOGFILE%

These methods are described in the following section.


The logfile

A logfile is a simple database of submissions of your form. Formage needs it for several features: limiting the total number of submissions, limiting duplicates, printing out all the entries so far, etc. It's also handy if you want to save the original data from your form submissions. To tell Formage where your logfile is, place a line like the following in your template:

%LOGFILE% = /afs/ir/users/y/o/yourname/web/logs/myform.log

The webserver will need write permission (see above) in the directory. Each time a form is submitted, Formage will write the set of variables for that submission to the logfile.

By default, it will write all the user variables from your form and template. If you want system variables to be written to the logfile, list them in %LOGFILE_SYSVARS% (or the older name, %LOGFILE_VARS%):

%LOGFILE_SYSVARS% = %DATE%, %REFERER%, %REMOTE_HOST%, %REMOTE_USER, %USER_AGENT%

All variable names in the logfile are surrounded by @ signs. Each record is followed by a line of dashes between two @ signs to separate it from other records. For example, here's a logfile with two entries:

@DATE@=Fri Jan 10 17:38:05 1997
@REFERER@=http://www.stanford.edu/group/idg/leland/formage/templates/example3.fft
@REMOTE_HOST@=dahlia.stanford.edu
@REMOTE_USER@=
@USER_AGENT@=Mozilla/3.01Gold (X11; U; IRIX 5.3 IP22)
@author@=Umberto Eco
@book@=The Island of the Day Before
@name@=Conrad
@text@=
@what@=shimmery
@--------------------------------------@
@DATE@=Fri Jan 10 17:38:31 1997
@REFERER@=http://www.stanford.edu/group/idg/leland/formage/templates/example3.fft
@REMOTE_HOST@=dahlia.stanford.edu
@REMOTE_USER@=
@USER_AGENT@=Mozilla/3.01Gold (X11; U; IRIX 5.3 IP22)
@author@=Stanley Elkin
@book@=The Rabbi of Lud
@name@=Conrad
@text@=Some nattering so
you can see that variables with line
returns keep them
@what@=funny
@--------------------------------------@

Logfiles have a tendency to grow large quickly. Part of the reason for that is that all user variables are written to the logfile by default. There are two ways to control which variables are written to the logfile, so that you can better manage its size:

%LOGFILE_FORMVARS_ONLY% is a toggle. If it's on (set to anything other than 0), then only variables that came from your form will be written to the logfile. Variables you create in your template will be left out.

%LOGFILE_FORMVARS_ONLY% = 1

%LOGFILE_USERVARS% can be used to give an explicit list of which user variables you want written to the logfile. That way you can limit it to only the ones you're really interested in.

%LOGFILE_USERVARS% = @name@, @age@

A warning will be issued if your logfile is greater than a certain size (currently 1 meg). In that case it's best to archive as much of it as you can, or eliminate unused fields. A large logfile will slow down your form.

You'll notice that @text@ in the first entry above was written even though it's blank. Variables that don't have values will be written to the logfile by default. If you don't want them in there, put the following line in your template:

%LOGFILE_NOEMPTIES% = 1

Normally Formage will only write to your logfile when people submit their forms (and not when they first get them), which is probably what you want. However, if you want more control, you can set the system variable %LOGFILE_WRITE% to either 0 (never write) or to 1 (always write).

%LOGFILE_WRITE% = 0
%LOGFILE_WRITE% = 1

For even more control, you can set %LOGFILE_WRITE% in a translation statement in your input filter (see below) so that it's based on the value of some other variable, such as %GOT_INPUT%.

Reading in the logfile

Sometimes you will want to be able to get at all the entries so far, rather than just the current one. For example, you may be taking registrations and you'd like to have a web page that shows all the ones you've received so far, including the current one. The records are read in in the order they were written to the logfile. To write out logfile records, use the special variable &OUTPUT_LOGFILE:RecordFormat& (see below).

Once the logfile has been read in, the total number of records will be available in %LOGFILE_ENTRIES%. That variable is useful if you want to limit the total number of form submissions by using a filter (see below).

Selecting from and sorting the logfile

If you want to read in only certain records, you can define a filter that tells Formage which records to throw away and which ones to keep. That's done by placing a LOGFILE_SELECT directive in the ACTION block. You need to give it the name of a filter which it will use to select records.

LOGFILE_SELECT=[filter name]

Once Formage is done with the filtering, the number of records currently selected will be available in %LOGFILE_ENTRIES%.

You may also want the logfile arranged in a certain order. To do that, place a LOGFILE_ORDER directive in the ACTION block. You need to tell it at least two things: what field to sort on (the key), and how to do the sorting. You can sort either forward or in reverse, and either alphabetically or numerically, so there are six types of sorting:

  1. sorted: forward, alphabetically
  2. isorted: forward, alphabetically, ignoring case
  3. nsorted: forward, numerically
  4. reverse: backward, alphabetically
  5. ireverse: backward, alphabetically, ignoring case
  6. nreverse: backward, numerically

You can provide multiple pairs of methods and keys, separated by commas. The first key is usually called the primary key, the second is the secondary key, and you get bonus points for knowing what the third and fourth are called. For example,

LOGFILE_ORDER=sorted by @last_name@, sorted by @first_name@

within an action block is a common way to sort by last name, breaking ties by looking at the first name.

Both selection and sorting of the logfile happen within the context of an ACTION block. If you have multiple ACTION blocks performing those actions, each one starts with a fresh copy of the logfile.

Sorting by date: Normally that would be impossible due to the format of %DATE%, which starts with the day of the week. So there's a special hack to make it work. You'll need to include %DATE% in %LOGFILE_SYSVARS%, since @DATE@ is what Formage will look for. If you sort based on that field (alphabetically or numerically, it doesn't matter), Formage will convert it to a number first so that the comparison works. Or you could just save %DATE_NUMERIC% or %DATE_UNIX% in the logfile and sort on that.

Logfile keys

Formage maintains a key for each record in your logfile. It is available to you as %LOGFILE_ENTRY_NUM%. Normally it is just the position of the record in your logfile: the first record will have a %LOGFILE_ENTRY_NUM% of 1, etc. %LOGFILE_CURRENT% is different in that it's the position in the currently selected logfile records. For example, if you have a logfile with 20 records and you select 6 of them with a filter, %LOGFILE_ENTRY_NUM% will range from 1 to 20 and %LOGFILE_CURRENT% will range from 1 to 6.

A key can be used in updates to match a field to the record it belongs to:

Edit name:
<INPUT TYPE="text" NAME="name_%LOGFILE_ENTRY_NUM%" VALUE="@name@">

That will give the field a unique field name of something like "name_5", which is necessary if you're updating more than one record. It can be a very useful way of doing complex things with logfiles.

%LOGFILE_ENTRY_NUM% is not written to your logfile, so a record may get a different key the next time through if you've messed with the logfile. If you'd like each record to have a permanent key, set

%LOGFILE_ENTRY_NUM_KEY% = 1

and it will get written to your logfile and then used the next time your logfile is read by Formage. That way a record retains its key even if its position in the logfile changes.

Updating/rewriting the logfile

OUTPUT=logfile_update:/afs/ir/users/y/o/yourname/web/file.log

OUTPUT=logfile_rewrite:/afs/ir/users/y/o/yourname/web/file.log

This is something you won't normally need to do, but it's handy if you're doing something along the lines of database maintenance with your template (with the logfile being the database).

logfile_update makes it easy to update your logfile. It works with a filter in its action block. Use the filter to select and modify (via translation statements) logfile records. When that's done, the logfile_update method will take the changed records and fold them into your logfile.

<ACTION>
OUTPUT=logfile_update:%LOGFILE%
LOGFILE_SELECT=GetShimmery
</ACTION>

<FILTER NAME=GetShimmery>
@what@ == shimmery
@new_name@ =~ /./ => @author@ = @new_name@
</FILTER>

That says: For each book that was described as "shimmery", change the author to whatever the user typed into the form field "new_name".

You don't need to give the action a FORMAT since Formage knows to use its logfile format.

logfile_rewrite lets you delete records from your logfile. It also makes it easy to nuke your logfile, so be careful! It has a filter in its action block that you use to select the records that will remain. Those are written to the given logfile.

For this example, assume that the user just submitted a form with a checkbox for each record. Ones whose boxes are checked will be deleted. The value of each checkbox is based on the %LOGFILE_ENTRY_NUM%.

<ACTION>
OUTPUT=logfile_rewrite:%LOGFILE%
LOGFILE_SELECT=Delete
</ACTION>

<FILTER NAME=Delete>
%LOGFILE_ENTRY_NUM% != @delete@
</FILTER>

The filter above won't select the record whose box was checked, so it will not be included in the logfile that gets rewritten. You don't need to give the action a FORMAT since Formage knows to use its logfile format.

If your filter doesn't let any records through, you'll end up writing nothing to the target logfile. If that's your main logfile, that's bad news! See below for one way to protect yourself.


Special variables

We lied way back there when we said there are two types of Formage variables. We wanted to make sure you're committed enough to have got this far before telling you about the third type, special variables. They have the ability to do more complex things than substitute values.

You may be wondering: How do I print out the logfile? Turns out there's a special Formage variable for doing that:

&OUTPUT_LOGFILE:[format]&

All special variables take that form:

&KEYWORD:argument&

The argument you give depends on the special variable you're using.

Printing out the logfile

&OUTPUT_LOGFILE:[format]&

Just put that line where you want the logfile to go, which will be somewhere in a format block. All the currently selected records (which is all of them unless you used LOGFILE_SELECT) will be printed out right there. Each record will be formatted according to the format that you give as an argument.

While the logfile is being printed out, the number of the current record is available to you in %LOGFILE_CURRENT%. If you want to number the records, just use that in the format being used to display each record.

The only place you can use &OUTPUT_LOGFILE& is in a format. You can't use one in a filter (see below). You also can't nest them, i.e. the format you give to &OUTPUT_LOGFILE& can't contain another &OUTPUT_LOGFILE& variable.

Including a file

&INCLUDE:[file_path]&

&INCLUDE& will read a file in. It's subject to the same restriction as any file used by your template, which is that it must reside on the same AFS volume as your template.

&INCLUDE:/~user/footer.html&
&INCLUDE:@include_file@&

Counting duplicates

&DUPLICATES:[variable]&

This special variable will be set to the number of times (based on looking through the logfile) that the variable has had its current value. The current value will usually come from a user who has just submitted your form. The most useful place to check duplicates is in a filter, to make sure for example that a person only submits your form once, or that a professor is chosen as an advisor by no more than three people. Remember, "duplicates" means how many times a field's value is repeated, so if you only want five of something, limit the duplicates to four. If a field must have a unique value, limit the duplicates to zero. Leading and trailing spaces are stripped from the fields before they're compared to see if they're duplicates.

Totalling the values of a variable

&SUM:[variable]&

&SUM& will return the total of a variable from the logfile. That total includes the current value if someone is submitting the form. For example, if you have a form that people use to reserve varying numbers of seats in a room, you can make sure that you don't have more seats reserved than are available:

&SUM:@seats@& <= 150 ## Not enough seats!

Counting the number of unique values of a variable

&UNIQUE:[variable]&

&UNIQUE& will return the number of unique values for a variable from the logfile. The value from the form counts if the form is being submitted. This way you could figure out how many different degree programs are represented in your logfile, for example:

&UNIQUE:@major@& different majors seen

Counting multivalue fields

&ITEMS:[user variable]&

Some form fields can take more than one value, for example a multiple selection list or a group of checkboxes with the same name. They will be joined with either commas (the default), or what you have given in the %MULTIVALUE% system variable. You might also want to know how many there were. To do that, use the ITEMS special variable above, giving it the name of the multivalue variable you want counted.


Filters

Filters let you do things based on the value one or more of your variables ends up with. For example, you can check to see if someone entered five digits into a "zip code" field, and return an error if they didn't. Another common use of a filter is to select records from a logfile based on the value of a field.

Filters consist of two different types of statements: validation and translation. A validation statement is either true or false, and a filter's validation statements taken together make the filter either succeed or fail. Whether a filter succeeds or fails can be used to control some sort of action, for example whether a record is selected from the logfile. Translation statements are used to change values conditionally. For example, you could add an "s" to the end of a word if there's more than one of something. Translation statements don't affect whether a filter succeeds or fails.

Validation statements take one of the following forms:

[cond] lvalue op rvalue
[cond] lvalue op rvalue ## helptext

Translation statements take the general form:

[cond] lvalue op rvalue => variable = value

There's a special case of the translation statement you can use just to give a variable a value (so you don't need to put something silly like "1 < 2" on the left side):

variable = value

The components surrounded by square brackets (such as [cond]) are optional. Each component is described below. First a quick definition: Formage text is text that can include Formage variables, which will be resolved (their values will be substituted in) before the text is used.

You can use arithmetic in Formage text. The following operations are available:

  • Addition: @a@ + 3
  • Subtraction: @a@ - @b@
  • Multiplication: @a@ * 2
  • Division: @c@ / 3
  • Modulus (remainder): @a@ % 10
  • Exponentiation (raise to power): @a@ ** 2

The arithmetic will only be done if the operands are numbers. In other words if you have

@a@ * 2

and @a@ is equal to "orange", you'll end up with the text "orange * 2". But if @a@ is equal to "13", you'll end up with "26".

Filter components

cond

  • AND
  • OR
  • SET

You can join validation statements with ORs and ANDs to create more complex validation steps. Association is done from top to bottom (left to right), so that A OR B AND C OR D is evaluated as (((A OR B) AND C) OR D). There is no grouping mechanism (i.e. parentheses) to set precedence. The default for validation statements is AND.

Translations and the setting of a variable (which is considered a degenerate case of translation) have an implicit SET rather than an AND or an OR. You can include it or not.

      <FILTER NAME=Voter>
          @age@ >= 18 ## Too young to vote!
          @party@ =~ /^Dem/ => @leaning@ = leftish
      AND @registered@ == yes ## Remember to register!
          @approved@ = %DATE%
      SET @send_pamphlet@ = yes
      </FILTER>
      

This filter succeeds only if the person is at least 18 and is registered to vote. The second line, a translation, will only be attempted if the person is 18 or older. The last two lines set variables only if both validation statements (first and third lines) succeeded.

One place ORs come in handy is in a search, if you want records that match any of the criteria set by the user. Below is a sample filter that could be used to select from the logfile:

      <FILTER NAME=Search>
          @business@ =~ /@business@/i
      OR  @city@ =~ /@city@/i
      OR  @state@ == @state@
      </FILTER>
      

Without the ORs, the validation statements are implicitly joined by ANDs, so all the validation statements would have to succeed for the record to be chosen.

You may wonder what is being compared above, for example, where @business@ comes from each time it is used. A variable's value can be found in the template, from user input, or in the logfile record. In the example above, Formage will look in the logfile for the value of @business@ on the left side, and in user input for the value on the right side. The fact that it uses a different order of places to look means you don't have to do something like change the name of the variable in your form to differentiate it from the one in the logfile.

lvalue

Formage text. It is often just the name of a field you are testing, but you can add text or do math if you want. Some handy lvalues are: form variable (for edit checking), %LOGFILE_ENTRIES% (for limiting total submissions), and &DUPLICATES:[user variable]&.

Formage will first look for the value of a variable in an lvalue in the logfile.

op

The comparison operator. Pretty much what you'd expect, with a couple of extras. The following comparison operators can be used (! means "not") :

  • == (equal to)
  • != (not equal to)
  • < (less than)
  • > (greater than)
  • <= (less than or equal to)
  • >= (greater than or equal to)

Those will work for both numbers and strings. The other two operators are used for Perl regular expressions:

  • =~ (matches)
  • !~ (does not match)

The rvalue side must be a Perl regular expression. For information on Perl regular expressions, try "man perlre", which is also available on the web from MIT, or look in any book on Perl. There are a quite a few regular expressions used in the examples. One common use is to say "has anything at all in it". For example, to make sure someone entered their name, you could say

@name@ =~ /./

By the way, you have to use slash (/) as your delimiter, and the only flag you can use is "i" for "ignore case".

An empty pattern in a filter makes the validation statement fail. If you have

      <FILTER NAME=Test>
      @name@ =~ /@letter@/ ## @name@ doesn't have @letter@
      </FILTER>
      

and @letter@ doesn't end up with anything in it, that validation statement will evaluate to false and a warning will be issued.

rvalue

The expression on the right side of the comparison operator can either be Formage text (the comparison operators) or a Perl regular expression (the other two). If both the lvalue and the rvalue are numbers, they will be compared numerically. Otherwise they will be compared as text (important when you remember that 10 comes before 2 when they're compared as text).

Formage will first look for the value of a variable in an rvalue in the user input.

helptext

Formage text. A brief bit of help to give the user in case they enter something that's not allowed.

variable

A user or system variable that is potentially going to be set to a value.

value

Formage text. Again, just some text which might have Formage variables in it. In a translation, variable gets set to value if the comparison on the left side of the ==> is true, and if the filter is true at that point.

Using filters

A filter is another block like the ones we've seen. Like a format, it has a name that is used to invoke it. The name is made up of letters, numbers, underscores, and dashes, same as a FORMAT name.

<FILTER NAME=TestFilter>
%LOGFILE_ENTRIES% <= 100
@zip@ =~ /^\d{5}$/ ## Enter your 5-digit zip code
&DUPLICATES:@univid@& == 0 ## @univid@ already submitted a form
@name@ == Robert => @name@ = Bob
</FILTER>

In English, the above filter does the following:

<FILTER NAME=TestFilter>
Only 100 submissions of this form.
Make sure zip code consists of five digits.
Only one form for each university ID number.
If it's Robert, call him Bob.
</FILTER>

What a filter does depends on where it's getting used. Remember that a filter can do both validation and translation - the effect those have depends on the context. The statements will be done in the order they appear in the filter. A filter has a state at any given point. It starts out as true. Each time a validation statement is evaluated, the state of the filter becomes either true or false. A translation statement will be skipped if the state of the filter at the time is false. This gives you a little more control. If you want all your translation statements to be looked at, put them at the beginning of the filter. The state of the filter at the end determines whether it has succeeded or failed.

A filter can be used on:

Input

One common use of a filter is to validate the user's input. (That's also called edit checking.) If a validation filter fails, an error will be returned to the user. The error page will include the help text (if any) you provide for all the things they entered that didn't pass your edit checking. Translation statements that succeed will change the input for all of the actions.

To tell Formage which filter to use on the user's input, use the %VALIDATE_INPUT% variable:

%VALIDATE_INPUT% = [filter name]

If you want just the first error to be returned (rather than a potentially long list of all of them), set the following system variable:

%SHOW_FIRST_ERROR% = 1

Logfile

A filter will also allow you to select records from the logfile. For example, you may want to display only the entries that came from Stanford machines, or only the ones in which the user chose green as their favorite color. A record must pass successfully through the filter, otherwise it gets tossed. Translation statements will affect the records within that action (for example when they are displayed), but not the original logfile.

You select from the logfile within an ACTION, so different actions may select from the logfile differently. Use the LOGFILE_SELECT directive (see above for details).

Formats

A FORMAT can use a filter to perform translations on the variables that will be used to fill it in. Validation statements have no effect - the record (whether it's current user input or a logfile record) has already been accepted. If you want the output to be translated differently for different formats, you can give those formats different filters. Performing translations on logfile records can be done in either a LOGFILE_SELECT filter or a FORMAT (the one used to display each record) filter.

To add a filter to a FORMAT, use the FILTER attribute:

<FORMAT NAME=MyForm FILTER=MyFilter>

Actions

An ACTION block can have a FILTER attribute just like a FORMAT does. If the filter fails, the action will not be performed.

To give an action a filter, use a line like:

<ACTION FILTER=MyFilter>

If you're using this feature you may very well want to perform just one of a set of actions. With Formage 1.0 you had to construct an opposite filter that would fail for any action you didn't want taken. Now you can tell Formage to make the current action the last one by setting %LAST_ACTION% to a non-zero value:

<ACTION FILTER=ActionFilter>
OUTPUT = reply
FORMAT = ActionOne
%LAST_ACTION% = 1
</ACTION>


Variable scoping

It's probably safe to go on to the next section. There's a good chance you won't need to know anything about scoping, but it's included for completeness and because in a few situations it can make a difference.

Scoping is a way to answer the question: When I use @name@, is that the "name" that was a field in my form, or the @name@ that I put in my template, or the @name@ I put in my action block, or the @name@ from a logfile record?

The short answer is: Whichever makes the most sense. Formage is written to look in what it thinks is the most appropriate place to find a variable. There are three basic scopes in Formage:

  • Global: Template variables (outside any block) and user input (form fields). The latter takes precedence, so if you set "@name@ = Fred" in your template and your form has a field called "name" that the user enters "Marsha" into, the value of @name@ will be "Marsha". You can use the value in the template as a default value, or you can just give it a default value in the form.

  • Current: These are variables that belong to a particular action. When the action is done, they're gone. For example, you can set variables within an action block that are only in play for that action.

  • Logfile: These are the variables that make up a logfile record. Understanding scoping is most important when your template is manipulating logfile records since they often have the same names as your form variables.

The two main times to worry about scoping are during format resolution (substituting values in for variables), and in filters. When Formage is resolving a format, it will look in the current scope for the value of a variable, and then in the global scope if it's not present in the current scope. When you're resolving a logfile record, it looks first in the logfile scope, then current, then global.

Filter scoping is similar. There's an additional need to differentiate the lvalue from the rvalue in a filter. That applies mostly to logfile selection. But first we'll look at a simple example. Assume we have an input form with a form field called "name".

<ACTION>
OUTPUT = reply
FORMAT = MyReply
@name@ = Wilbur
</ACTION>

<FORMAT NAME=MyReply>
Your name is @name@, maybe.
</FORMAT>

Let's say the user types "margaret" into the "name" field in the form. What will be printed? Since Formage looks first in the current scope and then in the global scope, you'll see

Your name is Wilbur, maybe.

A more realistic case is in logfile selection, since the same form that creates the logfile entries may be used to select from the logfile.

<ACTION>
OUTPUT = reply
FORMAT = MyReply
LOGFILE_SELECT = MyFilter
</ACTION>

<FILTER NAME=MyFilter>
@name@ == @name@
</FILTER>

In the filter above, @name@ as an lvalue will look first in the logfile scope (i.e. in the current logfile record), then in the current scope, and lastly in the global scope for its value. As an rvalue it will look in the global, then the current, and finally the logfile scope for its value. That way we don't always end up with the same value for both sides.

To sum up, here's the order in which scopes are checked to find the value for a variable during format resolution:

logfile (if applicable), current, global

Filters are a bit more complicated since there are more places a variable might show up, and those different places have different scoping rules. The chart below shows the order in which Formage looks for the value of a variable used in a filter statement. "where set" means in which scope a variable's value will be set as the result of a translation statement.

Filter typelvaluervaluewhere set
input validationglobalglobalglobal
actionglobalglobalglobal
formatlogfile*, current, globalglobal, current, logfile*current
logfile selectionlogfile, current, globalglobal, current, logfilelogfile
* if applicable

By the way, system variables (the ones surrounded by %) are also affected by scoping, but it's much less likely that you'll run into a problem with them. You can set system variables in the current scope by placing them in an action block. There's no logfile scope for system variables, so remove that from the explanations above to figure out where Formage will look for the value of a system variable.


Odds and ends

Using your own input form

You don't have to put the input form in your template. If you want, you can just call Formage from your input form by pointing the ACTION attribute of the <FORM> tag at your template.

How people can find you if it doesn't work

When something bad happens (for example, if a file can't be opened), Formage will return an error to the user. If you want your address to show up in that error message, use %ADMIN_EMAIL% :

%ADMIN_EMAIL% = [your email address]

Of course, your address should also appear on the input form or somewhere nearby where it's easily found.

Escaping HTML

If you're creating a web page from form submissions, you'll need to decide whether to allow users to add HTML tags to your file. They may add bad HTML that messes up the display of your page. For example, an unclosed <H1> tag will make the rest of the file big and bold and ugly. You can make Formage escape the HTML, which will cause the tags to just be displayed rather than used to mark up the text. To turn on HTML escaping, use %ESCAPE_HTML% :

%ESCAPE_HTML% = 1

Joining multivalue fields

Some form fields can take more than one value, for example a multiple selection list or a group of checkboxes with the same name. By default, Formage will display those with commas and spaces in between: value1, value2, value3

To do something different, tell Formage what you want on the left, on the right, and how to separate the values by giving a little template to the %MULTIVALUE% variable. Use the string xxx to stand for a value. Below are two examples with how their output would look.

%MULTIVALUE% = [xxx xxx]                   [value1 value2 value3]

%MULTIVALUE% = <OL><LI>xxx<LI>xxx</OL>     <OL><LI>value1<LI>value2<LI>value3</OL>

What to do when a file can't be opened

First, make sure that the path is correct. What you see in the error message has been "resolved", so it may not be the same as what's in your template file. It should end up as the absolute path of the file you're trying to use.

Make sure that the server has the necessary AFS permissions to read from and/or write to that file. To check them, use the command

fs listacl [dir]

The server can get access through "system:anyuser", "system:authuser", or "system:www-servers". The last one is the most secure one to use.

Also, make sure you haven't run out of disk space (quota):

fs listquota [dir]

If everything's hunky-dory so far, then AFS itself may be having problems. You may want to see if you can interact with your files in other ways. If you have questions about AFS and the commands above, check the AFS documentation or contact the Sweet Hall consultants.

If you get the message "The file [file] is currently in use. Please wait and try again.", then someone has submitted your form and that file is being updated. If you see that message indefinitely, you may need to remove the lock on that file. You'll notice a directory called "[file].LOCKDIR" in the same directory as the file. To remove it, go to the directory and type:

rm -r [file].LOCKDIR

Wrapping text

If you'd like your text formatted so that the lines don't go over a certain length, set the system variable %WORDWRAP% to that length:

%WORDWRAP% = 40

Line returns - Macs, PCs, and Unix machines

They're a pain in the butt. Each platform has its own idea of what a line return should look like. Since Formage is running in a Unix environment and it won't know what the form provider is using, it uses the Unix line return character: \n (10 in ASCII). Don't worry about your template file - you can create that on any of the three platforms and Formage will figure it out. However, Formage will only use Unix line returns when producing output. User input from a different platform as well as a template from a different platform will have their line returns converted to Unix line returns before anything is sent or written to a file.

Special characters

You may be wondering how to represent something like a space or a percent sign in a format or translation without confusing Formage. You do that by escaping the character with a backslash. Formage recognizes the following escaped characters:

NameCharacterEscape
backslash\\\
at sign@\@
percent sign%\%
return\n
space\s
tab\t
ampersand&\&
less than<\<
greater than>\>

Authenticated use

If you need to know who's filling out your form in a way you can trust, or if you need to restrict its use to Stanford folks, you can protect your template file using the campus webauth method. It's easy to use. Just create a file called .htaccess in the directory and put the following two lines in it:

AuthType StanfordAuth
require valid-user

If you want to restrict access to a list of users, give their Leland usernames (separated by spaces) instead of "valid-user".

Authenticated use changes the behavior of Formage in several ways. Several of the mailing restrictions are lifted: You can send mail off-campus and you can use Formage variables in the mailto: address. Normally the mail that gets sent has a From: header that looks something like:

From: Anonymous Web User <wwwd@leland.Stanford.EDU>

If the user has authenticated, the From: header will look something like:

From: Fred Hargadon <fredh@stanford.edu>

Their name and email address will be grabbed from the directory. If the name isn't found, it will be "Authenticated Web User". If no email address is found, it will be set to "sunetid@stanford.edu", where "sunetid" is the sunetid of the authenticated user.

Built-in formats

Well, there's only one so far, the Formage logfile format. A built-in format saves you the trouble of coming up with the format on your own. Say you'd like to write to a file using one of the normal file_* output methods, but you'd like it to look like a logfile. You could come up with a FORMAT that mimics the Formage logfile format, but it you make a mistake it won't work as a logfile. Instead you can just say

FORMAT = FormageLogfileFormat

This gives you a way to write the current entry to a backup logfile. If you're doing updates and especially if you're doing rewrites on the logfile, it's a good idea to keep a backup version with all the original data. You can do that by having each entry also get appended to a separate file using the logfile format:

%LOGFILE% = file.log

<ACTION>
OUTPUT=file_append:%LOGFILE%.backup
FORMAT=FormageLogfileFormat
</ACTION>

Of course you'll want to only write the variables you'll need, so that your logfile and its backup don't grow too large and slow your form down.

Customized error messages

You may want to provide the user with more information in case something goes wrong, whether it's a user error or not. To use a custom error message, put it in a format and tell Formage the name of the format:

%ERROR_FORMAT% = MyErrorFormat

The text of the actual error message that Formage generated is available to you in %ERROR%.

File upload

File upload is a way for a browser to send a file to a webserver via a form. A special type of form input called "file" is used, and it will cause most browsers to put up a text box with a "Browse" button next to it to make it easy for the user to find a file on their computer. After the file is selected and the form is submitted, the browser will send the file to the webserver.

You can use Formage to tell the webserver where the uploaded file should go, and what it should be named. Since it's possible (though rarely used) to upload more than one file in a form, Formage uses the following naming scheme to match the uploaded file with where it should go:

The form variable should be upload_file followed by a number. For example, upload_file1:

Some file: <INPUT TYPE="file" NAME="upload_file1">

The destination of the uploaded file goes in a Formage system variable %UPLOAD_TARGET% followed by a number, such as %UPLOAD_TARGET1%:

%UPLOAD_TARGET1% = /afs/ir/users/u/s/user/files/somefile.txt

Formage will match up the numbers to determine which uploaded file gets saved as which target file. The form variable will contain the name of the file that was sent (just the filename, no directory components), so you can use it in the name of the target file if you want:

%UPLOAD_TARGET1% = /afs/ir/users/u/s/user/files/@upload_file1@

You can limit the maximum size of a file that someone can upload using your template:

%UPLOAD_FILE_MAX_SIZE% = 500

The size is in kilobytes (K), so the maximum above is 500K, or half a meg. That can help prevent someone from filling up your AFS quota. Of course, since they must be webauth'ed to upload a file, you can log %SU_AUTH_USER% and see who they are.


Converting from Mailmerge to Formage

Mailmerge templates had four basic parts: the ACTION line, an INPUT_FORM, an OUTPUT_FORM, and miscellaneous variables. Each of these has its corollary in Formage.

The ACTION line becomes an ACTION block. The action itself becomes the OUTPUT part of the action block, and a FORMAT needs to be added to format the output. For example, the following Mailmerge action

ACTION=mailto:youraddress@leland.stanford.edu

when translated to Formage, becomes something like

<ACTION>
OUTPUT=mailto:youraddress@leland.stanford.edu
FORMAT=MyForm
</ACTION>

The "File" method in Mailmerge becomes "file_append" in Formage, and the "Nothing" method becomes "reply".

The INPUT_FORM becomes just another FORMAT block, but you have to give it a name so you can refer to it in the INPUT_FORM block:

<INPUT_FORM>
FORMAT=MyInputForm
</INPUT_FORM>

The OUTPUT_FORM also becomes a named FORMAT block. Just refer to it by name in an action block when you want to use it, with a line like

FORMAT=MyFormat

Variables in Mailmerge are all of the same type, so there's no need to use @ or % (or even &) when setting them in the template. In Formage, you need to decide what type of variable you're using. In general, if it's one you made up, it's a user variable and it should always be surrounded by @. If it's one that Formage is providing, it's a system variable and should always be surrounded by %. Notice that Mailmerge only required the use of @ when you wanted a variable's value substituted in. Formage always requires the @.

Below is a simple Mailmerge template and how it would look as a Formage template.

# The Mailmerge template

ACTION=mailto:youraddress@leland.stanford.edu

INPUT_FORM=
<H2>Memo</H2>
To: <STRONG>aliens@venus</STRONG><BR>
From: <INPUT TYPE="text" NAME="From" VALUE="@From@" SIZE=40><BR>
Subject: <INPUT TYPE="text" NAME="SUBJECT" VALUE="@SUBJECT@" SIZE=40><BR>
Priority: <INPUT TYPE="radio" NAME="PRIORITY" VALUE="High"> High
          <INPUT TYPE="radio" NAME="PRIORITY" VALUE="Normal" CHECKED> Normal
          <INPUT TYPE="radio" NAME="PRIORITY" VALUE="Plaid"> Plaid
<P>
<TEXTAREA ROWS=10 COLS=50 NAME="BODY">
</TEXTAREA>
<P>
<INPUT TYPE="submit" VALUE="Send">
.

OUTPUT_FORM=
                      * MEMO *
Priority: @PRIORITY@

@BODY@
.

SUBJECT=(no subject)
PRIORITY=Normal
From=some form filler-outer
TITLE=Send mail to Venus

# The same template as a Formage template.

<INPUT_FORM>
FORMAT=InputForm
</INPUT_FORM>

<ACTION>
OUTPUT=mailto:youraddress@leland.stanford.edu
FORMAT=MailMessage
</ACTION>

<FORMAT NAME=InputForm>
<H2>Memo</H2>
To: <STRONG>aliens@venus</STRONG><BR>
From: <INPUT TYPE="text" NAME="From" VALUE="@From@" SIZE=40><BR>
Subject: <INPUT TYPE="text" NAME="SUBJECT" VALUE="@SUBJECT@" SIZE=40><BR>
Priority: <INPUT TYPE="radio" NAME="PRIORITY" VALUE="High"> High
          <INPUT TYPE="radio" NAME="PRIORITY" VALUE="Normal" CHECKED> Normal
          <INPUT TYPE="radio" NAME="PRIORITY" VALUE="Plaid"> Plaid
<P>
<TEXTAREA ROWS=10 COLS=50 NAME="BODY">
</TEXTAREA>
<P>
<INPUT TYPE="submit" VALUE="Send">
</FORMAT>

<FORMAT NAME=MailMessage>
                      * MEMO *
Priority: @PRIORITY@

@BODY@
</FORMAT>

@SUBJECT@=(no subject)
%MAIL_SUBJECT% = @SUBJECT@
@PRIORITY@=Normal
@From@=some form filler-outer
@TITLE@=Send mail to Venus
Last modified Friday, 21-Sep-2007 11:23:54 AM

Stanford University Home Page