diff options
Diffstat (limited to 'src/mailman/rest/docs')
| -rw-r--r-- | src/mailman/rest/docs/__init__.py | 97 | ||||
| -rw-r--r-- | src/mailman/rest/docs/domains.rst | 38 | ||||
| -rw-r--r-- | src/mailman/rest/docs/listconf.rst | 13 | ||||
| -rw-r--r-- | src/mailman/rest/docs/systemconf.rst | 1 | ||||
| -rw-r--r-- | src/mailman/rest/docs/templates.rst | 539 |
5 files changed, 651 insertions, 37 deletions
diff --git a/src/mailman/rest/docs/__init__.py b/src/mailman/rest/docs/__init__.py index 113e299c3..5a039ac3d 100644 --- a/src/mailman/rest/docs/__init__.py +++ b/src/mailman/rest/docs/__init__.py @@ -17,8 +17,103 @@ """Doctest layer setup.""" +import threading + +from http.server import BaseHTTPRequestHandler, HTTPServer from mailman import public +from mailman.testing.helpers import wait_for_webservice from mailman.testing.layers import RESTLayer -public(layer=RESTLayer) +# New in Python 3.5. +try: + from http import HTTPStatus +except ImportError: # pragma: no cover + class HTTPStatus: + FORBIDDEN = 403 + NOT_FOUND = 404 + OK = 200 + + +# We need a web server to vend non-mailman: urls. +class TestableHandler(BaseHTTPRequestHandler): + # Be quiet. + def log_request(*args, **kws): + pass + + log_error = log_request + + def do_GET(self): # pragma: no cover + if self.path == '/welcome_2.txt': + if self.headers['Authorization'] != 'Basic YW5uZTppcyBzcGVjaWFs': + self.send_error(HTTPStatus.FORBIDDEN) + return + response = TEXTS.get(self.path) + if response is None: + self.send_error(HTTPStatus.NOT_FOUND) # pragma: no cover + return + self.send_response(HTTPStatus.OK) + self.send_header('Content-Type', 'UTF-8') + self.end_headers() + self.wfile.write(response.encode('utf-8')) + + +class HTTPLayer(RESTLayer): + httpd = None + + @classmethod + def setUp(cls): + assert cls.httpd is None, 'Layer already set up' + cls.httpd = HTTPServer(('localhost', 8180), TestableHandler) + cls._thread = threading.Thread(target=cls.httpd.serve_forever) + cls._thread.daemon = True + cls._thread.start() + wait_for_webservice('localhost', 8180) + + @classmethod + def tearDown(cls): + assert cls.httpd is not None, 'Layer not set up' + cls.httpd.shutdown() + cls.httpd.server_close() + cls._thread.join() + + +public(layer=HTTPLayer) + + +# Response texts. +WELCOME_1 = """\ +Welcome to the "$list_name" mailing list! + +To post to this list, send your email to: + + $fqdn_listname + +There is a Code of Conduct for this mailing list which you can view at +http://www.example.com/code-of-conduct.html +""" + +WELCOME_2 = """\ +I'm glad you made it! +""" + +WELCOME_3 = """\ +Je suis heureux que vous pouvez nous rejoindre! +""" + +WELCOME_4 = """\ +Welcome to the $list_name list in the $domain domain. +""" + +WELCOME_5 = """\ +Yay! You joined the $fqdn_listname mailing list. +""" + + +TEXTS = { + '/welcome_1.txt': WELCOME_1, + '/welcome_2.txt': WELCOME_2, + '/ant.example.com/fr/welcome_3.txt': WELCOME_3, + '/welcome_4.txt': WELCOME_4, + '/welcome_5.txt': WELCOME_5, + } diff --git a/src/mailman/rest/docs/domains.rst b/src/mailman/rest/docs/domains.rst index 89c03d170..82b7dc7cd 100644 --- a/src/mailman/rest/docs/domains.rst +++ b/src/mailman/rest/docs/domains.rst @@ -26,19 +26,16 @@ initially none. Once a domain is added, it is accessible through the API. :: - >>> domain_manager.add( - ... 'example.com', 'An example domain', 'http://lists.example.com') - <Domain example.com, An example domain, base_url: http://lists.example.com> + >>> domain_manager.add('example.com', 'An example domain') + <Domain example.com, An example domain> >>> transaction.commit() >>> dump_json('http://localhost:9001/3.0/domains') entry 0: - base_url: http://lists.example.com description: An example domain http_etag: "..." mail_host: example.com self_link: http://localhost:9001/3.0/domains/example.com - url_host: lists.example.com http_etag: "..." start: 0 total_size: 1 @@ -46,39 +43,30 @@ Once a domain is added, it is accessible through the API. At the top level, all domains are returned as separate entries. :: - >>> domain_manager.add( - ... 'example.org', - ... base_url='http://mail.example.org') - <Domain example.org, base_url: http://mail.example.org> + >>> domain_manager.add('example.org',) + <Domain example.org> >>> domain_manager.add( ... 'lists.example.net', - ... 'Porkmasters', - ... 'http://example.net') - <Domain lists.example.net, Porkmasters, base_url: http://example.net> + ... 'Porkmasters') + <Domain lists.example.net, Porkmasters> >>> transaction.commit() >>> dump_json('http://localhost:9001/3.0/domains') entry 0: - base_url: http://lists.example.com description: An example domain http_etag: "..." mail_host: example.com self_link: http://localhost:9001/3.0/domains/example.com - url_host: lists.example.com entry 1: - base_url: http://mail.example.org description: None http_etag: "..." mail_host: example.org self_link: http://localhost:9001/3.0/domains/example.org - url_host: mail.example.org entry 2: - base_url: http://example.net description: Porkmasters http_etag: "..." mail_host: lists.example.net self_link: http://localhost:9001/3.0/domains/lists.example.net - url_host: example.net http_etag: "..." start: 0 total_size: 3 @@ -91,12 +79,10 @@ The information for a single domain is available by following one of the ``self_links`` from the above collection. >>> dump_json('http://localhost:9001/3.0/domains/lists.example.net') - base_url: http://example.net description: Porkmasters http_etag: "..." mail_host: lists.example.net self_link: http://localhost:9001/3.0/domains/lists.example.net - url_host: example.net You can also list all the mailing lists for a given domain. At first, the example.com domain does not contain any mailing lists. @@ -154,30 +140,26 @@ New domains can be created by posting to the ``domains`` url. Now the web service knows about our new domain. >>> dump_json('http://localhost:9001/3.0/domains/lists.example.com') - base_url: http://lists.example.com description: None http_etag: "..." mail_host: lists.example.com self_link: http://localhost:9001/3.0/domains/lists.example.com - url_host: lists.example.com And the new domain is in our database. :: >>> domain_manager['lists.example.com'] - <Domain lists.example.com, base_url: http://lists.example.com> + <Domain lists.example.com> # Unlock the database. >>> transaction.abort() -You can also create a new domain with a description, a base url, and a contact -address. +You can also create a new domain with a description and a contact address. :: >>> dump_json('http://localhost:9001/3.0/domains', { ... 'mail_host': 'my.example.com', ... 'description': 'My new domain', - ... 'base_url': 'http://allmy.example.com' ... }) content-length: 0 content-type: application/json; charset=UTF-8 @@ -186,15 +168,13 @@ address. ... >>> dump_json('http://localhost:9001/3.0/domains/my.example.com') - base_url: http://allmy.example.com description: My new domain http_etag: "..." mail_host: my.example.com self_link: http://localhost:9001/3.0/domains/my.example.com - url_host: allmy.example.com >>> domain_manager['my.example.com'] - <Domain my.example.com, My new domain, base_url: http://allmy.example.com> + <Domain my.example.com, My new domain> # Unlock the database. >>> transaction.abort() diff --git a/src/mailman/rest/docs/listconf.rst b/src/mailman/rest/docs/listconf.rst index 6804644ac..922da5ea4 100644 --- a/src/mailman/rest/docs/listconf.rst +++ b/src/mailman/rest/docs/listconf.rst @@ -36,6 +36,8 @@ All readable attributes for a list are available on a sub-resource. default_member_action: defer default_nonmember_action: hold description: + digest_footer_uri: + digest_header_uri: digest_last_sent_at: None digest_send_periodic: True digest_size_threshold: 30.0 @@ -44,8 +46,10 @@ All readable attributes for a list are available on a sub-resource. display_name: Ant filter_content: False first_strip_reply_to: False + footer_uri: fqdn_listname: ant@example.com goodbye_message_uri: + header_uri: http_etag: "..." include_rfc2369_headers: True join_address: ant-join@example.com @@ -63,13 +67,11 @@ All readable attributes for a list are available on a sub-resource. reply_goes_to_list: no_munging reply_to_address: request_address: ant-request@example.com - scheme: http send_welcome_message: True subject_prefix: [Ant] subscription_policy: confirm volume: 1 - web_host: lists.example.com - welcome_message_uri: mailman:///welcome.txt + welcome_message_uri: Changing the full configuration @@ -109,7 +111,6 @@ When using ``PUT``, all writable attributes must be included. ... posting_pipeline='virgin', ... filter_content=True, ... first_strip_reply_to=True, - ... goodbye_message_uri='mailman:///goodbye.txt', ... convert_html_to_plaintext=True, ... collapse_alternatives=False, ... reply_goes_to_list='point_to_list', @@ -117,7 +118,6 @@ When using ``PUT``, all writable attributes must be included. ... send_welcome_message=False, ... subject_prefix='[ant]', ... subscription_policy='moderate', - ... welcome_message_uri='mailman:///welcome.txt', ... default_member_action='hold', ... default_nonmember_action='discard', ... moderator_password='password', @@ -162,8 +162,8 @@ These values are changed permanently. display_name: Fnords filter_content: True first_strip_reply_to: True + footer_uri: fqdn_listname: ant@example.com - goodbye_message_uri: mailman:///goodbye.txt ... include_rfc2369_headers: False ... @@ -177,7 +177,6 @@ These values are changed permanently. subject_prefix: [ant] subscription_policy: moderate ... - welcome_message_uri: mailman:///welcome.txt Changing a partial configuration diff --git a/src/mailman/rest/docs/systemconf.rst b/src/mailman/rest/docs/systemconf.rst index fa8b7384b..385588077 100644 --- a/src/mailman/rest/docs/systemconf.rst +++ b/src/mailman/rest/docs/systemconf.rst @@ -12,6 +12,7 @@ get a list of all defined sections. You can also get all the values for a particular section. >>> dump_json('http://localhost:9001/3.0/system/configuration/mailman') + cache_life: 7d default_language: en email_commands_max_lines: 10 filtered_messages_are_preservable: no diff --git a/src/mailman/rest/docs/templates.rst b/src/mailman/rest/docs/templates.rst new file mode 100644 index 000000000..f5d6773dc --- /dev/null +++ b/src/mailman/rest/docs/templates.rst @@ -0,0 +1,539 @@ +=========== + Templates +=========== + +In Mailman 3.1 a new template system was introduced to allow for maximum +flexibility in the format and content of messages sent by and through Mailman. +For example, when a new member joins a list, a welcome message is sent to that +member. The welcome message is created from a template found by a URL +associated with a template name and a context. + +So if for example, you want to include links to pages on you website, you can +create a custom template, make it available via download from a URL, and then +associate that URL with a mailing list's welcome message. Some standard +placeholders can be defined in the template, and these will be filled in by +Mailman when the welcome message is sent. + +The URL itself can have placeholders, and this allows for additional +flexibility when looking up the content. + + +Examples +======== + +Let's say you have a mailing list:: + + >>> ant = create_list('ant@example.com') + +The standard welcome message doesn't have any links to it because by default +Mailman doesn't know about any web user interface front-end. When Anne is +subscribed to the mailing list, she sees this plain welcome message. + + >>> anne = subscribe(ant, 'Anne') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Ant" mailing list + From: ant-request@example.com + To: Anne Person <aperson@example.com> + ... + <BLANKLINE> + Welcome to the "Ant" mailing list! + <BLANKLINE> + To post to this list, send your email to: + <BLANKLINE> + ant@example.com + <BLANKLINE> + You can make such adjustments via email by sending a message to: + <BLANKLINE> + ant-request@example.com + <BLANKLINE> + with the word 'help' in the subject or body (don't include the + quotes), and you will get back a message with instructions. You will + need your password to change your options, but for security purposes, + this email is not included here. There is also a button on your + options page that will send your current password to you. + +Let's say though that you wanted to provide a link to a Code of Conduct in the +welcome message. You publish both the code of conduct and the welcome message +pointing to the code on your website. Now you can tell the mailing list to +use this welcome message instead of the default one. + + >>> call_http('http://localhost:9001/3.1/lists/ant.example.com/uris', { + ... 'list:user:notice:welcome': 'http://localhost:8180/welcome_1.txt', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + +The name of the template corresponding to the welcome message is +`list:user:notice:welcome` and the location of your new welcome message text +is at `http://localhost:8080/welcome_1.txt`. + +Now when a new member subscribes to the mailing list, they'll see the new +welcome message. + + >>> bill = subscribe(ant, 'Bill') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Ant" mailing list + From: ant-request@example.com + To: Bill Person <bperson@example.com> + ... + <BLANKLINE> + Welcome to the "Ant" mailing list! + <BLANKLINE> + To post to this list, send your email to: + <BLANKLINE> + ant@example.com + <BLANKLINE> + There is a Code of Conduct for this mailing list which you can view at + http://www.example.com/code-of-conduct.html + +It's even possible to require a username and password (Basic Auth) for +retrieving the welcome message. + + >>> call_http('http://localhost:9001/3.1/lists/ant.example.com/uris', { + ... 'list:user:notice:welcome': 'http://localhost:8180/welcome_2.txt', + ... 'username': 'anne', + ... 'password': 'is special', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + +The username and password will be used to retrieve the welcome text. + + >>> cris = subscribe(ant, 'Cris') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Ant" mailing list + From: ant-request@example.com + To: Cris Person <cperson@example.com> + ... + <BLANKLINE> + I'm glad you made it! + +The text is cached so subsequent uses don't necessarily need to hit the +internet. + + >>> dave = subscribe(ant, 'Dave') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Ant" mailing list + From: ant-request@example.com + To: Dave Person <dperson@example.com> + ... + <BLANKLINE> + I'm glad you made it! + + +Template format +=============== + +Mailman expects the templates to be return as content type +`text/plain; charset="UTF-8"`. + +Template URLs can be any of the following schemes: + +* `http://` - standard scheme supported by the requests_ library; +* `https://` - standard scheme also supported by requests_; +* `file:///` - any path on the local file system; UTF-8 contents by default; +* `mailman:///` - a path defined within the Mailman source code tree. It is + not recommended that you use these; they are primarily provided for + `Mailman's internal use`_. + +Generally, if a template is not defined or not found, the empty string is +used. IOW, a missing template does not cause an error, it simply causes the +named template to be blank. + + +URL placeholders +================ + +The URLs themselves can contain placeholders, and this can be used to provide +even more flexibility in the way the template texts are retrieved. Two common +placeholders include the List-ID and the mailing list's preferred language +code. + + >>> ant.preferred_language = 'fr' + >>> call_http('http://localhost:9001/3.1/lists/ant.example.com/uris', { + ... 'list:user:notice:welcome': + ... 'http://localhost:8180/$list_id/$language/welcome_3.txt', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + +The next person to subscribe will get a French welcome message. + + >>> dave = subscribe(ant, 'Elle') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + Subject: =?iso-8859-1?q?Welcome_to_the_=22Ant=22_mailing_list?= + From: ant-request@example.com + To: Elle Person <eperson@example.com> + ... + <BLANKLINE> + Je suis heureux que vous pouvez nous rejoindre! + +Standard URL substitutions include: + +* `$list_id` - The mailing list's List-ID (`ant.example.com`) +* `$listname` - The mailing list's fully qualified list name + (`ant@example.com`) +* `$domain_name` - The mailing list's domain name (`example.com`) +* `$language` - The language code for the mailing list's preferred language + (`fr`) + + +Template contexts +================= + +When Mailman is looking for a template, it always searches for it in up to +three *contexts*, and you can set the template for any of these three +contexts: a mailing list, a domain, the site. + +Most templates are searched first by the mailing list, then by domain, then by +site. One notable exception is the ``domain:admin:notice:new-list`` template, +which is sent when a new mailing list is created. Because (modulo any style +default settings) there won't be a template for the newly created mailing +list, this template is always searched for first in the domain, and then in +the site. + +In fact, this illustrates a common naming scheme for templates. The +colon-separated sections usually follow the form +``<context>:<recipient>:<type>:<name>`` where ``context`` would be "domain" or +"list, ``<recipient>`` would be "admin", "user", or "member", and ``<type>`` +can be "action" or "notice". This isn't a strict naming scheme, but it does +give you some indication as to the use of the template. All template names +used internally by Mailman are given below. + +You've already seen how the mailing list context works above. Let's look at +the domain and site contexts next. + + +Domain context +-------------- + +Let's say you want all mailing lists in a given domain to share exactly the +same welcome message template. Remember that Mailman will insert +substitutions into the templates themselves to customize them for each mailing +list, so in general a single template can be shared by all mailing lists in +the domain. + +The first thing to do is to set the URI for the welcome message in the domain +to be shared. + + >>> call_http('http://localhost:9001/3.1/domains/example.com/uris', { + ... 'list:user:notice:welcome': + ... 'http://localhost:8180/welcome_4.txt', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + +And let's create a new mailing list in this domain. + + >>> bee = create_list('bee@example.com') + +Now when Anne subscribes to the Bee mailing list, she will get this +domain-wide welcome message. + + >>> anne = subscribe(bee, 'Anne') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Bee" mailing list + From: bee-request@example.com + To: Anne Person <aperson@example.com> + ... + Welcome to the Bee list in the example.com domain. + +So far so good. What happens if Fred subscribes to the Ant mailing list? + + >>> fred = subscribe(ant, 'Fred') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + Subject: =?iso-8859-1?q?Welcome_to_the_=22Ant=22_mailing_list?= + From: ant-request@example.com + To: Fred Person <fperson@example.com> + ... + <BLANKLINE> + Je suis heureux que vous pouvez nous rejoindre! + +Okay, that's strange! Why did Fred get the French welcome message? It's +because the mailing list context overrides the domain context! Similarly, a +domain context overrides a site context. This allows you to provide generic +templates to be used as a default, with specific overrides where necessary. + +Let's delete the Ant list's override. + + >>> ant.preferred_language = 'en' + >>> call_http('http://localhost:9001/3.1/lists/ant.example.com/uris' + ... '/list:user:notice:welcome', + ... method='DELETE') + content-length: 0 + date: ... + server: ... + status: 204 + +Now when Gwen subscribes to the Ant list, she gets the domain's welcome +message. + + >>> gwen = subscribe(ant, 'Gwen') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Ant" mailing list + From: ant-request@example.com + To: Gwen Person <gperson@example.com> + ... + <BLANKLINE> + Welcome to the Ant list in the example.com domain. + + +Site context +------------ + +Let's say we want the same welcome template for every mailing list on our +Mailman installation. For this we use the site context. + +First, let's delete the domain context we set previously. Note that +previously we used a `DELETE` method on the list's welcome template resource, +but we could have also done this by PATCHing an empty string for the URI, +which Mailman's REST API interprets as a deletion too. Let's use this +approach to delete the domain welcome message. + + >>> call_http('http://localhost:9001/3.1/domains/example.com/uris', { + ... 'list:user:notice:welcome': '', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + +Now let's set a new welcome template URI for the site. + + >>> call_http('http://localhost:9001/3.1/uris', { + ... 'list:user:notice:welcome': + ... 'http://localhost:8180/welcome_5.txt', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + +Now Herb subscribes to both the Ant... + + >>> herb = subscribe(ant, 'Herb') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Ant" mailing list + From: ant-request@example.com + To: Herb Person <hperson@example.com> + ... + <BLANKLINE> + Yay! You joined the ant@example.com mailing list. + +...and Bee mailing lists. + + >>> herb = subscribe(bee, 'Herb') + >>> items = get_queue_messages('virgin') + >>> print(items[0].msg) + MIME-Version: 1.0 + Content-Type: text/plain; charset="us-ascii" + Content-Transfer-Encoding: 7bit + Subject: Welcome to the "Bee" mailing list + From: bee-request@example.com + To: Herb Person <hperson@example.com> + ... + <BLANKLINE> + Yay! You joined the bee@example.com mailing list. + + +Templated texts +=============== + +All the texts that Mailman uses to create or decorate messages can be +associated with a URL. Mailman looks up templates by name and downloads it +via that URL. The retrieved text supports placeholders which are filled in by +Mailman. There are a common set of placeholders most templates support: + +* ``listname`` - fully qualified list name (e.g. ``ant@example.com``) +* ``list_id`` - the ``List-ID`` header (e.g. ``ant.example.com``) +* ``display_name`` - the display name of the mailing list (e.g. ``Ant``) +* ``short_listname`` - the local part of the list name (e.g. ``ant``) +* ``domain`` - the domain name part of the list name (e.g. ``example.com``) +* ``description`` - the mailing list's short description text +* ``info`` - the mailing list's longer descriptive text +* ``request_email`` - the email address for the ``-request`` alias +* ``owner_email`` - the email address for the ``-owner`` alias +* ``site_email`` - the email address to reach the owners of the site +* ``language`` - the two letter language code for the list's preferred + language (e.g. ``en``, ``it``, ``fr``) + +Other template substitutions are described below the template name listed +below. Here are all the supported template names: + +* ``domain:admin:notice:new-list`` + Sent to the administrators of any newly created mailing list. + +* ``list:admin:action:post`` + Sent to the list administrators when moderator approval for a posting is + required. + + * ``subject`` - the original ``Subject`` of the message + * ``sender_email`` - the poster's email address + * ``reasons`` - some reasons why the post is being held for approval + +* ``list:admin:action:subscribe`` + Sent to the list administrators when moderator approval for a subscription + request is required. + + * ``member`` - display name and email address of the subscriber + +* ``list:admin:action:unsubscribe`` + Sent to the list administrators when moderator approval for an + unsubscription request is required. + + * ``member`` - display name and email address of the subscriber + +* ``list:admin:notice:subscribe`` + Sent to the list administrators to notify them when a new member has + been subscribed. + + * ``member`` - display name and email address of the subscriber + +* ``list:admin:notice:unrecognized`` + Sent to the list administrators when a bounce message in an unrecognized + format has been received. + +* ``list:admin:notice:unsubscribe`` + Sent to the list administrators to notify them when a member has been + unsubscribed. + + * ``member`` - display name and email address of the subscriber + +* ``list:member:digest:footer`` + The footer for a digest message. + +* ``list:member:digest:header`` + The header for a digest message. + +* ``list:member:digest:masthead`` + The digest "masthead"; i.e. a common introduction for all digest + messages. + +* ``list:member:regular:footer`` + The footer for a regular (non-digest) message. + + When personalized deliveries are enabled, these substitution variables are + also defined: + + * ``member`` - display name and email address of the subscriber + * ``user_email`` - the email address of the recipient + * ``user_delivered_to`` - the case-preserved email address of the recipient + * ``user_language`` - the description of the user's preferred language + (e.g. "French", "English", "Italian") + * ``user_name`` - the recipient's display name if available + +* ``list:member:regular:header`` + The header for a regular (non-digest) message. + + When personalized deliveries are enabled, these substitution variables are + also defined: + + * ``member`` - display name and email address of the subscriber + * ``user_email`` - the email address of the recipient + * ``user_delivered_to`` - the case-preserved email address of the recipient + * ``user_language`` - the description of the user's preferred language + (e.g. "French", "English", "Italian") + * ``user_name`` - the recipient's display name if available + +* ``list:user:action:confirm`` + The message sent to subscribers when a subscription confirmation is + required. + + * ``token`` - the unique confirmation token + * ``subject`` - the ``Subject`` heading for the confirmation email, which + includes the confirmation token + * ``confirm_email`` - the email address to send the confirmation response + to; this corresponds to the ``Reply-To`` header + * ``user_email`` - the email address being confirmed + +* ``list:user:notice:goodbye`` + The notice sent to a member when they unsubscribe from a mailing list. + +* ``list:user:notice:hold`` + The notice sent to a poster when their message is being held or moderator + approval. + + * ``subject`` - the original ``Subject`` of the message + * ``sender_email`` - the poster's email address + * ``reasons`` - some reasons why the post is being held for approval + +* ``list:user:notice:no-more-today`` + Sent to a user when the maximum number of autoresponses has been reached + for that day. + + * ``sender_email`` - the email address of the poster + * ``count`` - the number of autoresponse messages sent to the user today + +* ``list:user:notice:post`` + Notice sent to a poster when their message has been received by the + mailing list. + + * ``subject`` - the ``Subject`` field of the received message + +* ``list:user:notice:probe`` + A bounce probe sent to a member when their subscription has been disabled + due to bounces. + + * ``sender_email`` - the email address of the bouncing member + +* ``list:user:notice:refuse`` + Notice sent to a poster when their message has been rejected by the list's + moderator. + + * ``request`` - the type of request being rejected + * ``reason`` - the reason for the rejection, as provided by the list's + moderators + +* ``list:user:notice:welcome`` + The notice sent to a member when they are subscribed to the mailing list. + + * ``user_name`` - the display name of the new member + * ``user_email`` - the email address of the new member + + +.. _requests: http://docs.python-requests.org/en/master/ +.. _`Mailman's internal use`: https://gitlab.com/mailman/mailman/blob/master/src/mailman/utilities/i18n.py#L45 |
