5 Dec 2004
This document discusses LISTSERV's mail and static web interface templates as they exist in version 14.3. It is beyond the scope of this document to discuss modification or management of LISTSERV's dynamic web interface templates, which is a separate and distinct subject.
Significant changes have been made both to the mail template processor and to the default mail templates themselves for the version 14.3 release. In the main, these changes allow an unprecedented level of control over what were previously hard-coded, non-optional internal messages sent by LISTSERV in response to various commands.
It is now possible to define site-wide defaults for all template forms without having to edit the DEFAULT MAILTPL file, which is not upgrade-safe. Any customizations made at the site level to the default templates now go into the site-level SITE MAILTPL file, which will not be disturbed during an upgrade. Defining site-wide defaults in the old $SITE$ MAILTPL file, which in any case did not work for all templates and was never intended for this use to begin with, is now deprecated, and support for such usage will be removed in LISTSERV 15.
During startup, LISTSERV 14.3 will migrate existing templates from WWW_ARCHIVE MAILTPL to SITE MAILTPL, unless one of the following conditions arises:
If there are no conflicts, WWW_ARCHIVE MAILTPL is then renamed to WWW_ARCHIVE OLDTPL. If a conflict is detected, LISTSERV will not attempt to determine which version of the template is "correct", but rather will log something like the following:
7 Oct 2004 10:57:05 Migrating templates from WWW_ARCHIVE to SITE...
- $TEST_TEMPLATE: conflicting version found in SITE
7 Oct 2004 10:57:05 Conflicts detected, finish migration manually
It is then incumbent on the site maintainer to harmonize the differing versions of any templates reported to be in conflict.
In LISTSERV 14.3 there are three different types of templates controlling administrative messages: mail templates, message templates, and message fragments. A fourth type of template, the static Web templates, controls the “.html” files created by LISTSERV in the Web Archives directory.
A mail template is, as in previous versions of LISTSERV, a "complete" mail message. All commands are available, substitutions that make sense in the context of the specific message are available, the message may be formatted, and while other templates may be imbedded with the .IM command, the message is in and of itself ready for LISTSERV to send.
The next level down is a message template, that is, a template that by default does not send any mail but supplies text that will ultimately be shown to the user - not always by e-mail, it could be over the web interface for instance.
Typically, message templates that are sent by mail will be "bundled" into a single message. However, a given message template can be forced to send an individual unbundled mail by using the new .SM command.
In a message template, you have the following restrictions:
The lowest level is a message fragment. It could be a list of months in French, or a common sequence of words that is put in a template not just to make other templates more readable, but to improve consistency (see &MSGREF). This fragment is used in another messages and is not itself a message, hence the following restrictions:
>>> Mail template "MSG_SUBSCRIBE_FRAGMENT_OPTCHANGED1" does not support .SM
and no email is actually sent. In the example case, .SM would have to be specified in the message template MSG_SUBSCRIBE_SUCCESS in order for it to succeed.
The following naming convention for message templates and message fragments has been introduced:
A static Web page template defines the HTML code for the “static” Web interface pages. These pages are “static” because they are in “.html” files, and – unlike the “dynamic” Web pages generated on the fly by the “wa” Web interface CGI – do not change according to the circumstances under which they are accessed. LISTSERV must create these pages because they require information that LISTSERV has, but is not necessarily available to all accounts using the Web interface, such as list configuration information. For example, LISTSERV knows that a “join” link should not be put on the Web page for a list with Subscription=Closed, but wa should not be able to access list header information when being used by a non-owner.
Static Web page templates use the same commands and some of the same variables as the mail templates, and are stored in MAILTPL files. The dynamic Web templates use different commands and variables (not covered in this document).
(As of the 14.3 release. Other templates may be added in future builds.)
$SIGNUP Standard message to new subscribers
$WWW_ARCHIVE_HEADER Header text for WWW archive interface
$WWW_ARCHIVE_TRAILER Trailer text for WWW archive interface
$WWW_IMAGES_URL URLs for standard images
$WWW_IMAGES_URLDEF -Default URLs for standard images, do not change
$WWW_STYLE_SHEET Style sheet used by WWW_INDEX and WWW_ARCHIVE_INDEX
$WWW_STYLE_SHEET_THEME1 Static Theme 1
$WWW_STYLE_SHEET_THEME2 Static Theme 2
$WWW_STYLE_SHEET_THEME3 Static Theme 3
$WWW_STYLE_SHEET_THEME4 Static Theme 4
$WWW_STYLE_SHEET_THEME5 Static Theme 5
$WWW_STYLE_SHEET_THEME6 Static Theme 6
$WWW_STYLE_SHEET_THEME7 Static Theme 7 (Big Print)
$WWW_STYLE_SHEET_THEME8 Static Theme8 (default font)
$WWW_STYLE_SHEET_THEME9 Static Theme 9 (no style sheet)
ADD1 You have been added to the &LISTNAME list
ADD2 &LISTNAME: &WHOM joined the list
ADDMOD2 Change in your name registration for the &LISTNAME list
ADDPW1 Your new password for &MYSELF
ADDPW2 Addition of a LISTSERV password for &WHOM
ADDPW3 Request for addition of a LISTSERV password
ADDREQ1 &LISTNAME: &WHOM requested to join
AUTODEL1 Your removal from the &LISTNAME list
AUTODEL2 &LISTNAME: Daily error monitoring report
CHANGE1 Change in your e-mail address for the &LISTNAME list
CONFIRM1 Text of confirmation requests
CONFIRM2 Command confirmation request cancelled
DELETE1 Your removal from the &LISTNAME list
DELPW Removal of your password for &MYSELF
DIRECTORY Template directory for X-GETTPL (overrides only)
HTML_DIGEST HTML digest preamble
HTML_INDEX HTML index preamble
INFO Information about the &LISTNAME list
MOVE1 Change in your host server for the &LISTNAME list
MSG_AVSCAN_FRAGMENT_SPAM1 Spam detected by AV scanner
MSG_AVSCAN_FRAGMENT_VIRUS1 Virus detected by AV scanner
MSG_POSTING_FORWARD_EDITOR Message sent to user when posting is forwarded to editor
MSG_POSTING_HOLD_EXCEED_OVERALL_LIMIT Sent to owner when list is held
MSG_POSTING_REJECT_BAD_ATTACHMENT Received an attachment not allowed by "Attachments="
MSG_POSTING_REJECT_CONTENTFILTER Message rejected by content filter
MSG_POSTING_REJECT_EXCEED_SIZELIM Posting exceeding "Sizelim=" limit
MSG_POSTING_REJECT_SPAM_DETECTED Received a message identified as spam
NONHTML_INDEX_EPILOGUE Epilogue for non-HTML index
PROBE1 Subscription probe for &LISTNAME - please ignore
PROBE2 Probe failure for &LISTNAME
RENEW1 Renewal of your subscription to the &LISTNAME list
RENEW2 Expiration of your subscription to the &LISTNAME list
REQACK1 Your message to &LISTNAME-request@&MYHOST
REQNAK1 Your message to ALL-request@&MYHOST
SETINFO Change in your subscription options for the &LISTNAME list
SIGNOFF1 &LISTNAME: &WHOM left the list
SIGNOFF2 Your request to sign off the &LISTNAME list
SIGNOFF_CONFIRM1 Text of confirmation requests (SIGNOFF)
SIGNOFF_CONFIRM2 Command confirmation request cancelled (SIGNOFF)
SIGNUP1 You are now subscribed to the &LISTNAME list
SUBSCRIBE_CONFIRM1 Text of confirmation requests (SUBSCRIBE)
SUBSCRIBE_CONFIRM2 Command confirmation request cancelled (SUBSCRIBE)
WWW_ARCHIVE_DIRECTORY -Template directory for X-GETTPL (WWW_ARCHIVE only)
WWW_ARCHIVE_HEADER Header file included by the CGI script
WWW_ARCHIVE_INDEX WWW archive interface, INDEX.HTML
WWW_ARCHIVE_TRAILER Trailer file included by the CGI script
WWW_ARCHIVE_USER_FORMS A list of additional "user" forms to format
WWW_INDEX HTML for the main list archive screen (listname.html)
WWW_NAVIGATION Navigation bar used by WWW_ARCHIVE_INDEX and WWW_INDEX
Any line starting with a period in column 1 is processed as a formatting command. Note that neither substitutions nor formatting commands are case sensitive. Here is a list of the formatting commands list owners may need to use:
Comment: anything on this line is simply ignored. This is useful for recording changes to template files when there are multiple owners. It is recommended to add a comment line with the date and your initials every time you make a change, for the benefit of the other owners.
.CC OFF removes all "cc:" message recipients, if any. In this context "cc:" is a RFC822 term that stands for "courtesy copy". RFC822 messages may have "cc:" recipients in addition to their "primary" recipients. There is no real technical difference between the two; the "cc:" indicator just denotes a message that is being sent for your information. Some administrative messages sent to list owners are copied to the user for their information, and vice-versa; this behavior can be disabled by adding a .CC OFF statement to the template.
You can also add message recipients by specifying a series of e-mail addresses after the .CC statement, as in .CC JOE@XYZ.EDU.
Centers the text you specify (just the text you typed on the same line as the .CE command). This can be useful to highlight the syntax of a command.
Turns off formatting: one template line = one line in the final message. You can resume formatting with .FO ON or .FO RAGGed.
Changes left-and-right-justified text formatting (“block” formatting) to left justified text formatting. You can resume left-and-right-justified formatting with .FO ON. (.FO RAGGed requires LISTSERV 14.1 [1.8e-2002a] or later, that is, build date of 31 October 2002 or later)
Cancels the message. LISTSERV stops reading the template form and does not send anything. This is useful if you want to completely remove a particular message; note however that this can be confusing with certain commands, as LISTSERV may say "Notification is being sent to the list owners" when in fact nothing will be sent because of the .QQ command in the template form. A .QQ command in an included template will cancel a message just the same as one in the main message template.
(Starting with 14.0 [1.8e]) Ends processing of the current template as if you had reached the end, but without cancelling the message. The main purpose is to avoid multi-level nested .BB/.EB conditional blocks (see below) that are hard to keep track of.
Adds a 'Reply-To:' field pointing to the list owners in the header of the generated message. Use this command when you think users are likely to want to reply with a question. You can also use .RE POSTMASTER to direct replies to the LISTSERV administrator, if this is more appropriate.
Replaces the default recipients of a message with the value specified. For instance, if you use the ADDREQ1 template form to send new subscribers a questionnaire, application form or similar material, you will need to add a '.TO &WHOM' instruction to your modified template form, as by default the user will not receive a copy.
A number of more advanced commands are available to list owners with more sophisticated needs and some programming experience. If you encounter one of these commands in a template, you will probably want to leave it alone.
Tells LISTSERV to leave the text immediately following the .ASIS directive alone, that is, don't convert "<" and ">" characters into HTML < and > when creating pages. This is specifically for use in HTML templates where it is important not to convert parts of a URL reference. For instance,
.ASIS Click <a href="http://some.host.com/some-doc.html">here</a>.
As with the .CE directive, the text you intend to affect with the .ASIS directive must not wrap. The .ASIS directive will only work on text it finds on the same physical line into which it is coded.
Begin conditional block.
Related commands are .EB, .ELSE, and .QU . See the section below entitled "Conditional processing" for usage.
See also .QUIF .
Define a (non standard) character set for the template in question, i.e.,
This setting is ignored if the template does not actually contain special characters (for instance, if the template is written in 7-bit ASCII). Otherwise the appropriate headers are created for the message in question when it is sent out.
Copies the contents of the specified DD into the message. This is meaningful only if a DD has been set up by LISTSERV for this purpose. As a rule of thumb, you should either leave these statements unchanged or remove them.
End conditional block (see .BB).
Conditional ELSE directive (see .BB).
Imbeds (inserts) another template form at this point in the message. This is used to avoid duplicating large pieces of text which are mostly identical, such as the templates for "you have been added to list X by Y" and "your subscription to list X has been accepted".
As noted below, LISTSERV will not pick up an "imbedded" template form from $SITE$.MAILTPL. If you wish to include an "imbedded" template form (e.g., $SIGNUP) in $SITE$.MAILTPL, you must also include the template form that calls it with the .IM command.
Stop (in other words, QUit) processing of the current template as if you had reached the end, but without cancelling the message. The main purpose is to avoid multi-level nested .BB/.EB conditional blocks that are hard to keep track of. Available in 14.0 (1.8e) and following.
(QUit IF) Similar to .QU, stop processing the current template without cancelling the message, based on the result of an inline conditional comparison. The full syntax is
.QUIF var operator value
.QUIF &RC = 0
This single statement is strictly equivalent to the block
.BB &RC = 0
.SE var text
Defines or redefines a substitution variable. This is convenient for storing temporary (text) expression results that need to be used several times. Even standard variables such as &LISTNAME can be redefined - at your own risk. You must enclose the text expression in single quotes if you want leading or trailing blanks.
Set the subject line for the message. .SJ works differently depending on the type of template:
Send a copy of the message via email, if echoed to the web interface.
.SM is not available in message fragments or static Web page templates, and is only available in message templates which do not contain a comment to the effect that .SM is not supported for that particular message template.
Types one line of text on the LISTSERV console log. This can be useful to the LISTSERV maintainer for debugging, and also to record information in the console log.
LISTSERV 14.3 mail templates can be programmed with an if/then/else logic that evaluates available data and performs the appropriate task depending on the outcome of the evaluation.
A conditional block begins with the command .BB (begin block) and ends with .EB (end block). In the simplest case, the boolean expression following .BB is evaluated and, if false, all the text between the .BB and .EB delimiters is skipped. For instance, from $SIGNUP:
.bb &DEFOPT ^= ''
Following instructions from the list owner, your subscription options
have been set to "&DEFOPT" rather than the usual LISTSERV defaults.
For more information about subscription options, send a "QUERY &LISTNAME"
command to &MYNAMES.
.ELSE may be specified in cases where an alternate text is required, as in this more complicated example (also from $SIGNUP):
.bb ((&x ^= POSTMASTER) and (&x ^= OWNER)) and (&x ^= OWNERS)
Please note that it is presently possible for
.bb &x = PUBLIC
to determine that you are signed up to the list through the use of the
"REVIEW" command, which returns the e-mail address and name of all
the subscribers. If you do not want your name to be visible,
just issue a "SET &LISTNAME CONCEAL" command.
Additionally, it is possible to simply exit a conditional at an arbitary point by using a .QU (QUit) command. For instance, the MSG_POSTING_REJECT_BAD_ATTACHMENT message template is used both to reject unwanted attachment types as well as viruses. In order to avoid processing the entire template (which contains other text that is not germane to a virus rejection), .QU is used to simply exit processing and send the message immediately.
.bb &VIRUS = 1
.* A virus was detected in the message. Note that this is only possible
.* for messages sent to a list, LISTSERV does not execute attachments so
.* messages sent to LISTSERV are not checked for viruses.
Your posting to the &LISTNAME list has been rejected because it contains
.bb &VIRUS_NAME ^= ''
.bb &VIRUS_FILENAME ^= ''
virus in attachment '&VIRUS_FILENAME;'.
You are strongly advised to check your computer for viruses as soon
.* text for rejecting attachments follows
The following should be noted:
.QUIF (QUit IF) allows the invoker to stop processing the template if a certain condition is met, but without having to define a full-blown conditional block. For instance, the .IM command used to imbed one template into another returns a success code in the template variable &RC. If imbedding succeeds, &RC is set to 0, and something like the following is possible:
>>> MSG_POSTING_REJECT_BAD_ATTACHMENT Received an attachment not allowed by "Attachments="
.* Use legacy BAD_ATTACHMENT template if present
.quif &rc = 0
.* at this point template processing stops and the message is sent if &RC = 0
.* otherwise processing would continue with any following text.
"quif &rc = 0" is strictly equivalent to the block
.bb &rc = 0
The DAYSEQ(n) function is quite powerful. This function allows the list owner to code template forms (such as the PROBE1 or BOTTOM_BANNER messages) that change or "rotate" automatically.
The DAYSEQ(n) function is invoked in a .BB - .EB conditional block, and n corresponds to the number of days in the rotation period, i.e., to the number of variations that you want to make to the text of the message. &DAYSEQ(n) returns a number from 1 to n which increases by 1 every day, with no special regard for weekends. That is, if the rotation period is to last for a week, you code DAYSEQ(7). If the rotation period is 15 days, you code DAYSEQ(15). Two examples follow:
To create a rotating bottom banner, follow this example. A list has three commercial sponsors, each of whom are provided with an advertisement every three days. (Note that this doesn’t take weekends into account; in this example, if company A is featured in the banner on Monday, it will be featured again on Thursday and then again on Sunday. However, in the following week it will be featured on Wednesday, Saturday, and Tuesday, so it will actually get rather good coverage.) Our BOTTOM_BANNER template form would look like this:
.BB &DAYSEQ(3) = 1
Today’s copy of the &LISTNAME newsletter has been brought to you
by Company A.
.BB &DAYSEQ(3) = 2
Today’s copy of the &LISTNAME newsletter has been brought to you by Company B.
.BB &DAYSEQ(3) = 3
Today’s copy of the &LISTNAME newsletter has been brought to you by Company C.
If a company needs to get a higher percentage of "air time” than another, you can simply assign it more than one of the possible n values of &DAYSEQ(n). For instance, if you have two companies but one should get twice as many days of "air time”, you might code something like this:
.BB (&DAYSEQ(3) = 1) OR (&DAYSEQ(3) = 3)
Today’s copy of the &LISTNAME newsletter has been brought to you
by Company A.
.BB &DAYSEQ(3) = 2
Today’s copy of the &LISTNAME newsletter has been brought to you by Company B.
This would cause Company A’s message to appear on days 1 and 3 of the rotation period and Company B’s message to appear on day 2 only.
Subscription renewal can be coded with daily granularity (however, please note that it is inadvisable to use renewal intervals of less than a week). If you further code subscription probing into the "Renewal=" keyword with the ",Probe" parameter, you open up the possibility of turning the standard PROBE1 template form into a periodic FAQ. Here’s how:
We’ll assume to start that you will code "Renewal= 15-Daily,Probe" in your list header. (You can experiment with other numbers, but since we have two messages and will be using &DAYSEQ(2), we need an odd renewal period.) We’ll also assume that you want to send two versions of your FAQ each month; the first, a complete FAQ document, and the second, an abbreviated "reminder" version that just contains information about how to sign off, how to post to the list, and so forth. The basic algorithm is therefore:
When &DAYSEQ(2) = 1, send the full FAQ.
When &DAYSEQ(2) = 2, as it will 15 days later, send the abbreviated FAQ.
Your PROBE1 template form would thus look like this:
>>> PROBE1 Periodic FAQ posting for &LISTNAME
&WEEKDAY, &DATE &TIME
.BB &DAYSEQ(2) = 1
This is the complete FAQ for &LISTNAME. Please read it and keep a copy for future reference. A FAQ document for &LISTNAME is distributed every 15 days, the full FAQ alternating with a shorter "reminder" FAQ.
<body of the full FAQ document>
.BB &DAYSEQ(2) = 2
This is the abbreviated FAQ for &LISTNAME. Please read it and keep a copy for future reference. A FAQ document for &LISTNAME is distributed every 15 days, the full FAQ alternating with a shorter "reminder" FAQ.
<body of the abbreviated FAQ document>
When you first start using a rotating banner with the &DAYSEQ variable, the &DAYSEQ(n)= 1 period begins based on the number of days elapsed since a baseline. This date format is also known as the "Gregorian serial day".
On VM (and in REXX generally) you can calculate today's value easily with:
say Date('B') + 1
If you do not have access to a REXX interpreter, Date('B') is described as "the number of complete days (that is, not including the current day) since and including the base date, 1 Jan 0001, in the format 'dddddd' (no leading zeros or blanks)."
It also is equal to the C language expression time(0)/86400 + 719162 or, for OpenVMS users, to the Smithsonian base date plus 678575.
For example, for Friday 22 Oct 2004, the value of Date('B') + 1 is 731876. This value increases by one every day at midnight.
The following substitutions are available in most templates:
Long-style date (04 Jan 1998)
The type of machine LISTSERV is running on, e.g., "Pentium (512M)".
Has the value 1 when running the LISTSERV Lite product, and 0 otherwise. This variable can be used to write generic templates that account for the differences between the two products.
Has the value 1 when running the Free Edition of LISTSERV Lite. Similar to but distinct from &LITE.
Looks up the specified address in LISTSERV's signup file and displays "name <addr>" if a name is available, or just the original address otherwise. This is typically used to give the name of the command originator or target, along with his e-mail address: &MBX(&WHOM) or &MBX(&INVOKER). Please note however that &WHOM and &INVOKER are not always available in every template.
The "addr" parameter is always required; &MBX by itself is syntactically invalid.
LISTSERV's Internet hostname or, if none is available, its NJE address (with '.BITNET').
The substitution you will use most of the time when you need to refer to LISTSERV. For Internet-only or BITNET-only servers, this will display LISTSERV's only e-mail address. For servers with both Internet and BITNET connectivity, it will say "LISTSERV@hostname (or LISTSERV@nodeid.BITNET)".
LISTSERV's BITNET nodeid, without the '.BITNET', or its Internet hostname if no NJE address is available.
LISTSERV's address, in the form LISTSERV@XYZ.EDU or, if no Internet hostname is available, LISTSERV@XYZVM1.BITNET.
The full operating system name including the version number, e.g., "VM/ESA 1.2.3", "Windows NT 4.0", "Linux 2.0.27", "SunOS 5.4", etc.
The operating system under which LISTSERV is running.
LISTSERV’s release number (e.g., "1.8e", "14.3").
Three-letter day of the week, in English
The following substitutions are also available for templates related to mailing lists:
Used to create FAQ templates with rotating topics. May also be used to create bottom banners with rotating text (e.g., for lists with multiple commercial sponsors who get "ad space" in the banner on a rotating basis).
Returns today’s date in ISO format, i.e., yyyy-mm-dd.
Value of the specified keyword for the list. You do not need to specify the name of the list - it is implicit. You need not put quotes around the keyword names either, although quotes will be accepted if present. Optionally, you can specify a second numeric argument to extract just one of the terms of a list header keyword; for instance, if the list header contains "Notebook= Yes,L1,Monthly,Private", &KWD(NOTEBOOK,4) has the value "Private". A third argument, also optional, specifies the default value for the keyword in case it was not initialized. It is meant to be used for conditional formatting in the default templates and list owners should not worry about it.
Either the short or long name of the list based on the value of "List-Address=" and/or its system default. By default the long ("List-ID=") name is used if present.
Title of the list, or empty string.
There are a number of variables that are available only in specific templates. In general, if the variable is available in a particular template, it can be found in the default version of that template. This list below is not exhaustive.
 Cowlishaw, Michael: The REXX Language: A Practical Approach to Programming, 2nd ed., p.92. Englewood Cliffs, NJ: Prentiss-Hall, Inc., 1990.