README.listtexts
List texts in Mlmmj
List texts are stored in listdir/text and subdirectories of prefix/share/mlmmj/text.skel. They specify the content of various automatic emails that Mlmmj sends. They are provided in a number of different languages. The language to use for a list is chosen when you run the mlmmj-make-ml script and the appropriate files are copied into your listdir/text directory.
This file documents the following aspects of list texts:
- Naming scheme
- Supported list texts
- Format
- Conditionals
- Wrapping
- Formatting and comments
- Formatted substitutions
- Unformatted substitutions
- Escapes
Naming scheme
List texts are named following a scheme of:
purpose-action-reason-type
Mlmmj will look for the full four-part name first, then for files with shorter names obtained by dropping parts off the end, and finally for a file with a compatibility filename. It will use the first one it finds. (Note that use of the compatibility filename is DEPRECATED and will be removed in a future release.)
So, the complete search order is:
- purpose-action-reason-type
- purpose-action-reason
- purpose-action
- purpose
- compatibility filename (DEPRECATED)
When using shortened names, the %ifaction%, %ifreason%, %iftype% and related conditionals can be used to customise the list text according to the values of the missing parts.
Mlmmj checks these three paths for each candidate filename, and then moves on to the next candidate filename:
- listdir/text
- prefix/share/mlmmj/text.skel/default
- prefix/share/mlmmj/text.skel/en
The second path does not exist by default, but can be created by copying or symlinking the language of your choice to that path.
Note that this search order means that if there is a more specific list text in a system directory, it will override a less-specific or compatibility list text in the listdir. This may be surprising, and may change in a future version, so should not be relied upon. Best practice is to ensure each list has its own copy of all textx present in system directories, or none of them.
Supported list texts
The following list texts are supported. The compatibility filename (DEPRECATED) is given in brackets. Those with asterisks (*) are not yet used.
help (listhelp) sent in response to an email to listname+help@domain.tld
faq (listfaq) sent in response to an email to listname+faq@domain.tld
confirm-sub-{request|admin}-normal (sub-confirm)
confirm-sub-{request|admin}-digest (sub-confirm-digest)
confirm-sub-{request|admin}-nomail (sub-confirm-nomail)
confirm-unsub-{request|admin}-normal (unsub-confirm)
confirm-unsub-{request|admin}-digest (unsub-confirm-digest)
confirm-unsub-{request|admin}-nomail (unsub-confirm-nomail) sent to a requester to allow them to confirm a (un-)subscription request
moderate-post-{modnonsubposts|access|moderated} (moderation) sent to the appropriate moderators when moderation is required because a user has submitted a post
gatekeep-sub-{request|admin|confirm}-{normal|digest|nomail} (submod-moderator) sent to the appropriate gatekeepers when gatekeeping is required because a subscription request has been received
wait-post-{modnonsubposts|access|moderated} (moderation-poster) sent to a person submitting a post when they need to wait for moderation before it is released to the list
wait-sub-{request|admin|confirm}-{normal|digest|nomail} (submod-requester) sent to a person requesting subscription when they need to wait for gatekeeping for permission to join
deny-sub-disabled-{digest|both} (sub-deny-digest)
deny-sub-disabled-nomail (sub-deny-nomail)
deny-sub-subbed-{normal|digest|nomail|both} (sub-subscribed)
deny-sub-closed *
deny-sub-expired *
deny-sub-obstruct *
deny-unsub-unsubbed-{normal|digest|nomail|all} (unsub-notsubscribed)
deny-post-subonlypost (subonlypost)
deny-post-modonlypost
deny-post-access (access)
deny-post-maxmailsize (maxmailsize)
deny-post-tocc (notintocc)
deny-post-expired *
deny-post-reject *
deny-release-notfound *
deny-release-moderators *
deny-reject-notfound *
deny-reject-moderators *
deny-permit-notfound *
deny-permit-gatekeepers *
deny-obstruct-notfound *
deny-obstruct-gatekeepers * sent to the requestor when an action is denied or fails for some reason (‘requestor’ here means the person who requested the action, so e.g. for a reject action, deny-reject will go to the moderator requesting the rejection if the rejection fails; but deny-post-reject will go to the person requesting the post if the rejection succeeds, causing the post to fail)
finish-sub-{request|confirm|admin|permit|switch}-normal (sub-ok)
finish-sub-{request|confirm|admin|permit|switch}-digest (sub-ok-digest)
finish-sub-{request|confirm|admin|permit|switch}-nomail (sub-ok-nomail)
finish-unsub-{request|confirm|admin}-normal (unsub-ok)
finish-unsub-{request|confirm|admin}-digest (unsub-ok-digest)
finish-unsub-{request|confirm|admin}-nomail (unsub-ok-nomail)
finish-post-request *
finish-post-confirm *
finish-post-release *
finish-release *
finish-reject *
finish-permit *
finish-obstruct * sent to the requestor when an action completes successfully (‘requestor’ here means the person who requested the action, so e.g. for a release action, the moderator requesting the release will receive finish-release, and the person who submitted the released post will receive finish-post-release because the release action caused their post action to succeed)
notify-sub-{request|confirm|admin|permit}-normal (notifysub)
notify-sub-{request|confirm|admin|permit}-digest (notifysub-digest)
notify-sub-{request|confirm|admin|permit}-nomail (notifysub-nomail)
notify-unsub-{request|confirm|admin|bouncing}-normal (notifyunsub)
notify-unsub-{request|confirm|admin|bouncing}-digest (notifyunsub-digest)
notify-unsub-{request|confirm|admin|bouncing}-nomail (notifyunsub-nomail) sent to the list owner when somebody is (un-)subscribed
digest sent at the start of a digest (NOTE: the only header supported in this list text so far is a single-line ‘Subject:’ header; however, the contents of control/customheaders is included when digests are sent)
probe (bounce-probe) sent to a subscriber after an email to them bounced to inform them of the bounce and probe when the address is no longer bouncing
list—all (listsubs)
list—normal *
list—digest *
list—nomail * sent in response to an email to listname+list@domain.tld from the list owner (DEPRECATED: if none of %listsubs%, %digestsubs% and %nomailsubs% is encountered in the text, then they will be automatically added with some default formatting; this functionality is expected to be removed in the future)
Not yet used.
Format
List texts have the following format:
- Headers
- Blank line
- Body
They are expected to be in UTF-8 encoding and have Unix line endings.
The headers should be formatted as they should appear in the mail message. They will begin the mail message. Header continuation via lines beginning with linear whitespace is supported.
Following the headers found in the list text, Mlmmj will output the following default headers, unless the same header is already provided in the list text.
- From:
- To:
- Message-ID:
- Date:
- Subject: mlmmj administrivia
- MIME-Version: 1.0
- Content-Type: text/plain; charset=utf-8
- Content-Transfer-Encoding: 8bit
The Subject: header is treated specially: it may include UTF-8 characters, which will automatically be escaped using the =?utf-8?q?…?= quoting mechanism.
(NOTE: the ‘digest’ list text is a bit different. See its description above.)
Both headers and bodies of list texts may include conditionals, formatting directives and substitutions. These are explained in the following sections.
Conditionals
Conditionals allow text in list texts to be included or omitted based on conditions. The following are available:
%ifaction A …% the action is one of those given
%ifreason R …% the reason is one of those given
%iftype T …% the type is one of those given
%ifcontrol C …% one of the given control files exists
%ifnaction A% the action is not the one given
%ifnreason R% the reason is not the one given
%ifntype T% the type is not the one given
%ifncontrol C …% at least one of the given control files does not exist
The text after the %if…% directive is only included if the condition is satisfied, until an %else% or %endif% is encountered. These behave as you would expect. The %else% is optional.
If a line with any of these conditional directives (%if…%, %else% or %endif%), after processing, contains only whitespace, the line does not appear at all in the output (the newline and any whitespace is omitted).
Furthermore, if the preceding processed output ends with a blank line, when an unsatisfied conditional is encountered which has no %else% part, that preceding blank line is removed (unless it is the blank line that ends the headers).
On the whole, this is what you would want and expect, so you probably don’t need to worry about it.
Note that when multiple parameters can be given for the directives, these have ‘or’ behaviour; to get ‘and’ behaviour, nest conditionals.
Wrapping
There are various directives available to assist with wrapping and formatting. Wrapping needs to be enabled for each paragraph with:
- %wrap%
- %wrap W% concatenate and rewrap lines until the next empty line, whitespace-only line, or %nowrap% directive to a width of W (or 76 if W is omitted); second and later lines are preceded with as many spaces as the width preceding the directive; the width is reckoned including any text preceding the directive and any indentation preserved from a file which included the current one, so it is an absolute maximum width
To turn off wrapping before the end of a paragraph, use:
- %nowrap% stop wrapping; usually placed at the end of a line so the following line break is honoured but all preceding text is properly wrapped; if you want wrapping to continue after the break, you need to use %wrap% to turn it on again on the following line
To cater for various languages, there are a number of different wrapping modes that can be set. These can be set either before or after wrapping is specified, and can even be changed part way through a paragraph if desired. The following directives control them:
%wordwrap%
%ww% use word-wrapping (this is the default; good for English, French, Greek and other languages that use an alphabet and spaces between words); lines have whitespace trimmed from both ends and are joined with a single space; lines are broken at spaces or at points marked for breaking with \/, but not at spaces escaped with a backslash
%charwrap%
%cw% use character-wrapping (good for Chinese, Japanese and Korean which use characters without spaces between words); lines have only leading whitespace trimmed and are joined without inserting anything at the joint; lines are broken at space or any non-ASCII character except where disallowed with =
%userwrap%
%uw% use user-wrapping (for more complex languages or wherever complete manual control is desired); lines have only leading whitespace trimmed and are joined without inserting anything at the joint; lines are broken only where marked for breaking with \/
%thin% assume non-ASCII characters are thin (equivalent to one unit, the same as ASCII characters) when reckoning the width for wrapping (this is the default; good for languages like Greek which use a non-Latin alphabet)
%wide% assume non-ASCII characters are wide (equivalent to two units, twice as wide as ASCII characters) when reckoning the width for wrapping (good for Chinese, Japanese, Korean)
%zero ABC% (ABC represents a sequence of non-ASCII characters) treat the listed characters as having zero-width when reckoning the width for wrapping (useful for ignoring combining characters such as accents so they don’t affect the width calculation); usefully, the listed characters can be represented as unicode escapes (\uNNNN)
If a line with any of the directives in this section, after processing, contains only whitespace, the line does not appear at all in the output (the newline and any whitespace is omitted).
Formatting and comments
The following directives are available to assist with formatting and readability:
%^% start the line here; anything preceding this directive is ignored (useful for using indentation for readability without ruining the formatting of the text when it is processed)
%comment%
%$% end the line here; anything following this directive is ignored/a comment
If a line with any of these directives, after processing, contains only whitespace, the line does not appear at all in the output (the newline and any whitespace is omitted).
Formatted substitutions
These formatted substitutions work with multiple lines, so are generally not appropriate for use in headers. They are:
%text T% text from the file named T in the listdir/text directory; the name may only include letters, digits, underscore, dot and hyphen, and may not start with a dot; note that there is an unformatted version of this directive
%control C% the contents of the control file named C in listir/control; the name may only include letters, digits, underscore, dot and hyphen, and may not start with a dot; note that there is an unformatted version of this directive
%originalmail%
%originalmail N% (available only in moderate-post-, wait-post- and deny-post-{access|maxmailsize|tocc|subonlypost|modonlypost}) the email message being processed (usually a mail being moderated); N represents a number, which is how many lines of the message (including headers) to include: if omitted, the whole message will be included
%digestthreads% (available only in digest) the list of threads included in the digest
%gatekeepers% (available only in gatekeep-sub and wait-sub) the list of moderators to whom the moderation request has been sent
%listsubs% (available only in list—*) the list of normal subscribers DEPRECATED: use %normalsubs%
%normalsubs% (available only in list—*) the list of normal subscribers
%digestsubs% (available only in list—*) the list of digest subscribers
%nomailsubs% (available only in list—*) the list of nomail subscribers
%moderators% (available only in moderate-post-* and wait-post-*) the list of moderators to whom the moderation request has been sent
%bouncenumbers% (available only in probe) the list of indexes of messages which may not have been received as they bounced
Directives which include a list of items have the behaviour that each item is preceded and followed by the same text as preceded and followed the directive on its line; only one such directive is supported per line. Those which include a block of text have the behaviour that second and later lines are preceded with as many spaces as there were bytes preceding the directive; any text following such directives on the same line is omitted.
If a line with any of these directives, after processing, contains only whitespace, the line does not appear at all in the output (the newline and any whitespace is omitted).
Unformatted substitutions
Unformatted substitutions that are available are:
$bouncenumbers$ (available only in probe) the formatted list of indexes of messages which may not have been received as they bounced DEPRECATED: use %bouncenumbers%
$confaddr$
$confirmaddr$ (available only in confirm-[un]sub-*) the address to which to send mail to confirm the (un-)subscription in question NOTE: the short version of this substitution is DEPRECATED
$control C$ the contents of the control file named C in listdir/control, with its final newline stripped; the name may only include letters, digits, underscore, dot and hyphen, and may not start with a dot; note that there is a formatted version of this directive
$digestfirst$ (available only in digest) index of the first message included in a digest
$digestinterval$ (available only in digest) indexes of the first and last messages included in a digest (e.g. 1-5), or just the index if only a single message is included
$digestissue$ (available only in digest) the issue number of the digest
$digestlast$ (available only in digest) index of the last message included in a digest
$digestsubaddr$ listname+subscribe-digest@domain.tld DEPRECATED: use $list+$subscribe-digest@$domain$ instead
$digestthreads$ (available only in digest) the formatted list of threads included in the digest DEPRECATED: use %digestthreads%
$digestunsubaddr$ listname+unsubscribe-digest@domain.tld DEPRECATED: use $list+$unsubscribe-digest@$domain$ instead
$domain$ domain.tld
$faqaddr$ listname+faq@domain.tld DEPRECATED: use $list+$faq@$domain$ instead
$helpaddr$ listname+help@domain.tld DEPRECATED: use $list+$help@$domain$ instead
$list$ listname
$list+$ listname+
$listaddr$ listname@domain.tld DEPRECATED: use $list$@$domain$ instead
$listgetN$ listname+get-N@domain.tld (the N here is nothing special, so this won’t actually work, but is used to explain to users how to use the +get functionality) DEPRECATED: use $list+$get-N@$domain$ instead
$listowner$ listname+owner@domain.tld DEPRECATED: use $list+$owner@$domain$ instead
$listsubaddr$ listname+subscribe@domain.tld DEPRECATED: use $list+$subscribe@$domain$ instead
$listunsubaddr$ listname+unsubscribe@domain.tld DEPRECATED: use $list+$unsubscribe@$domain$ instead
$maxmailsize$ (available only in deny-post-maxmailsize) the maximum size of mail that Mlmmj will accept
$moderateaddr$ (available only in moderate-post-* and gatekeep-sub) the address to which to send mail to approve the post or subscription in question DEPRECATED: use $releaseaddr$ or $permitaddr$ instead
$moderators$ (available only in moderate-post-, wait-post-, gatekeep-sub and wait-sub) the formatted list of moderators to whom the moderation request has been sent DEPRECATED: use %moderators% or %gatekeepers% instead
$newsub$ (available only in notify-sub--) the address that has been subscribed DEPRECATED: use $subaddr$ instead
$nomailsubaddr$ listname+subscribe-nomail@domain.tld DEPRECATED: use $list+$subscribe-nomail@$domain$ instead
$nomailunsubaddr$ listname+unsubscribe-nomail@domain.tld DEPRECATED: use $list+$unsubscribe-nomail@$domain$ instead
$oldsub$ (available only in notify-sub--) the address that has been unsubscribed DEPRECATED: use $subaddr$ instead
$originalmail$ the same as %originalmail 100% preceded by a space DEPRECATED: use %originalmail%
$permitaddr$ (available only in gatekeep-sub) the address to which to send mail to permit the subscription in question
$posteraddr$ (available only in deny-post-{access|tocc|subonlypost|modonlypost| maxmailsize}, moderate-post-* and wait-post-*) the from address of the message that was received as determined by Mlmmj
$random0$
$random1$
$random2$
$random3$
$random4$
$random5$ these are 6 distinct random strings; they allow list texts to be constructed that are MIME messages with attachments by creating boundaries that are unlikely to appear in the attached messages
$releaseaddr$ (available only in moderate-post-*) the address to which to send mail to release the post in question
$subaddr$ (available only in gatekeep-sub, confirm-[un]sub-, finish-[un]sub-, notify-[un]sub-* and deny-[un]sub-*) the address requested to be (un-)subscribed
$subject$ (available only in deny-post-{access|tocc|subonlypost|modonlypost| maxmailsize}, moderate-post-* and wait-post-*) the subject line of the message in question
$text T$ text from the file named T in the listdir/text directory, with its final newline stripped; the name may only include letters, digits, underscore, dot and hyphen, and may not start with a dot; note that there is a formatted version of this directive
Escapes
These allow you to avoid special meanings of characters used for other purposes in list texts, as well as control the construction of the texts at a fairly low level.
$$ a single $
%% a single %
\ a single \
\uNNNN (NNNN represents four hex digits) a Unicode character (this is not really appropriate for use in a header, except perhaps the Subject: header as Mlmmj does automatic quoting for that header as described above)
<space> a space, but don’t allow the line to be broken here when wrapping
\/ nothing, but allow the line to be broken here when wrapping
= nothing, but don’t allow the line to be broken here when wrapping