summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/database/schema/postgres.sql8
-rw-r--r--src/mailman/database/schema/sqlite.sql8
-rw-r--r--src/mailman/docs/NEWS.rst14
-rw-r--r--src/mailman/interfaces/mailinglist.py171
-rw-r--r--src/mailman/model/mailinglist.py8
-rw-r--r--src/mailman/mta/docs/decorating.rst33
-rw-r--r--src/mailman/pipeline/decorate.py39
-rw-r--r--src/mailman/pipeline/docs/decorate.rst108
-rw-r--r--src/mailman/runners/digest.py43
-rw-r--r--src/mailman/styles/default.py19
-rw-r--r--src/mailman/templates/en/footer-generic.txt4
11 files changed, 349 insertions, 106 deletions
diff --git a/src/mailman/database/schema/postgres.sql b/src/mailman/database/schema/postgres.sql
index 206f8f76f..b0a30bb78 100644
--- a/src/mailman/database/schema/postgres.sql
+++ b/src/mailman/database/schema/postgres.sql
@@ -48,8 +48,8 @@ CREATE TABLE mailinglist (
default_member_action INTEGER,
default_nonmember_action INTEGER,
description TEXT,
- digest_footer TEXT,
- digest_header TEXT,
+ digest_footer_uri TEXT,
+ digest_header_uri TEXT,
digest_is_default BOOLEAN,
digest_send_periodic BOOLEAN,
digest_size_threshold REAL,
@@ -59,12 +59,14 @@ CREATE TABLE mailinglist (
emergency BOOLEAN,
encode_ascii_prefixes BOOLEAN,
first_strip_reply_to BOOLEAN,
+ footer_uri TEXT,
forward_auto_discards BOOLEAN,
gateway_to_mail BOOLEAN,
gateway_to_news BOOLEAN,
generic_nonmember_action INTEGER,
goodbye_message_uri TEXT,
header_matches BYTEA,
+ header_uri TEXT,
hold_these_nonmembers BYTEA,
info TEXT,
linked_newsgroup TEXT,
@@ -74,8 +76,6 @@ CREATE TABLE mailinglist (
member_moderation_notice TEXT,
mime_is_default_digest BOOLEAN,
moderator_password TEXT,
- msg_footer TEXT,
- msg_header TEXT,
new_member_options INTEGER,
news_moderation INTEGER,
news_prefix_subject_too BOOLEAN,
diff --git a/src/mailman/database/schema/sqlite.sql b/src/mailman/database/schema/sqlite.sql
index d5ef06f0f..ae5145d7a 100644
--- a/src/mailman/database/schema/sqlite.sql
+++ b/src/mailman/database/schema/sqlite.sql
@@ -144,8 +144,8 @@ CREATE TABLE mailinglist (
default_member_action INTEGER,
default_nonmember_action INTEGER,
description TEXT,
- digest_footer TEXT,
- digest_header TEXT,
+ digest_footer_uri TEXT,
+ digest_header_uri TEXT,
digest_is_default BOOLEAN,
digest_send_periodic BOOLEAN,
digest_size_threshold FLOAT,
@@ -155,12 +155,14 @@ CREATE TABLE mailinglist (
emergency BOOLEAN,
encode_ascii_prefixes BOOLEAN,
first_strip_reply_to BOOLEAN,
+ footer_uri TEXT,
forward_auto_discards BOOLEAN,
gateway_to_mail BOOLEAN,
gateway_to_news BOOLEAN,
generic_nonmember_action INTEGER,
goodbye_message_uri TEXT,
header_matches BLOB,
+ header_uri TEXT,
hold_these_nonmembers BLOB,
info TEXT,
linked_newsgroup TEXT,
@@ -170,8 +172,6 @@ CREATE TABLE mailinglist (
member_moderation_notice TEXT,
mime_is_default_digest BOOLEAN,
moderator_password TEXT,
- msg_footer TEXT,
- msg_header TEXT,
new_member_options INTEGER,
news_moderation INTEGER,
news_prefix_subject_too BOOLEAN,
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst
index c1fdc948d..588642085 100644
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -26,16 +26,21 @@ Architecture
mailing list. The in-tree English templates are used only as a last
fallback.
* Support downloading templates by URI, including mailman:// URIs. This is
- used in welcome and goodbye messages, and supports both language and
- mailing list specifications. E.g. mailman:///test@example.com/it/welc.txt
+ used in welcome and goodbye messages, as well as regular and digest headers
+ and footers, and supports both language and mailing list specifications.
+ E.g. mailman:///test@example.com/it/welcome.txt
Database
--------
* Schema changes:
- - welcome_msg -> welcome_message_uri
- - goodbye_msg -> goodbye_message_uri
+ - welcome_msg -> welcome_message_uri
+ - goodbye_msg -> goodbye_message_uri
- send_welcome_msg -> send_welcome_message
- send_goodbye_msg -> send_goodbye_message
+ - msg_header -> header_uri
+ - msg_footer -> footer_uri
+ - digest_header -> digest_header_uri
+ - digest_footer -> digest_footer_uri
REST
----
@@ -59,6 +64,7 @@ Interfaces
argument to narrow the search for the given request.
* New `ITemplateLoader` utility.
* `ILanguageManager.add()` returns the `ILanguage` object just created.
+ * `IMailinglist.decorators` removed; it was unused
Commands
--------
diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py
index a7a964a06..4bd47a180 100644
--- a/src/mailman/interfaces/mailinglist.py
+++ b/src/mailman/interfaces/mailinglist.py
@@ -306,11 +306,6 @@ class IMailingList(Interface):
digest recipients are cleared.
""")
- decorators = Attribute(
- """An iterator over all the IDecorators associated with this digest.
- When a digest is being sent, each decorator may modify the final
- digest text.""")
-
# Web access.
scheme = Attribute(
@@ -334,24 +329,6 @@ class IMailingList(Interface):
'location' attribute.
"""
- # Notifications.
-
- admin_immed_notify = Attribute(
- """Flag controlling immediate notification of requests.
-
- List moderators normally get daily notices about pending
- administrative requests. This flag controls whether moderators also
- receive immediate notification of such pending requests.
- """)
-
- admin_notify_mchanges = Attribute(
- """Flag controlling notification of joins and leaves.
-
- List moderators can receive notifications for every member that joins
- or leaves their mailing lists. This flag controls those
- notifications.
- """)
-
# Autoresponses.
autoresponse_grace_period = Attribute(
@@ -518,13 +495,29 @@ class IMailingList(Interface):
# Notifications.
+ admin_immed_notify = Attribute(
+ """Flag controlling immediate notification of requests.
+
+ List moderators normally get daily notices about pending
+ administrative requests. This flag controls whether moderators also
+ receive immediate notification of such pending requests.
+ """)
+
+ admin_notify_mchanges = Attribute(
+ """Flag controlling notification of joins and leaves.
+
+ List moderators can receive notifications for every member that joins
+ or leaves their mailing lists. This flag controls those
+ notifications.
+ """)
+
send_welcome_message = Attribute(
"""Flag indicating whether a welcome message should be sent.""")
welcome_message_uri = Attribute(
"""URI for the list's welcome message.
- This can be any URI supported by `httplib2` with the addition of
+ This can be any URI supported by `urllib2` with the addition of
`mailman:` URIs, which reference internal default resources. This is
a template which can include the following placeholders:
@@ -551,14 +544,14 @@ class IMailingList(Interface):
goodbye_message_uri = Attribute(
"""URI for the list's goodbye message.
- This can be any URI supported by `httplib2` with the addition of
+ This can be any URI supported by `urllib2` with the addition of
`mailman:` URIs, which reference internal default resources. This is
a template which can include the following placeholders:
$listname - the FQDN list name for this mailing list.
$language - the language code, usually the list's preferred language.
- The resource will be downloaded and cached whenever the welcome
+ The resource will be downloaded and cached whenever the goodbye
message is sent. The resource at this URI can contain the following
placeholders, which are also filled in through values on the mailing
list:
@@ -572,6 +565,132 @@ class IMailingList(Interface):
$user_options_uri - the URI to this member's options page.
""")
+ # Decorators.
+
+ header_uri = Attribute(
+ """URI for the header decorator on regular delivery messages.
+
+ This can be any URI supported by `urllib2` with the addition of
+ `mailman:` URIs, which reference internal default resources. This is
+ a template which can include the following placeholders:
+
+ $listname - the FQDN list name for this mailing list.
+ $language - the language code, usually the list's preferred language.
+
+ The resource will be downloaded and cached whenever the decorator is
+ needed. The resource at this URI can contain the following
+ placeholders, which are also filled in through values on the mailing
+ list:
+
+ $fqdn_listname - the FQDN list name for this mailing list.
+ $list_name - the human readable name for the mailing list.
+ $host_name - the mailing list's host name
+ $listinfo_uri - the URI to the list's information page.
+ $list_requests - the address to the list's `-request` address.
+ $description - the mailing list's description
+ $info - additional mailing list's information
+
+ Personalized messages will also have these placeholders available:
+
+ $user_name - the name of the subscribing user.
+ $user_address - the email address of the subscribing user.
+ $user_options_uri - the URI to this member's options page.
+ """
+ )
+
+ footer_uri = Attribute(
+ """URI for the footer decorator on regular delivery messages.
+
+ This can be any URI supported by `urllib2` with the addition of
+ `mailman:` URIs, which reference internal default resources. This is
+ a template which can include the following placeholders:
+
+ $listname - the FQDN list name for this mailing list.
+ $language - the language code, usually the list's preferred language.
+
+ The resource will be downloaded and cached whenever the decorator is
+ needed. The resource at this URI can contain the following
+ placeholders, which are also filled in through values on the mailing
+ list:
+
+ $fqdn_listname - the FQDN list name for this mailing list.
+ $list_name - the human readable name for the mailing list.
+ $host_name - the mailing list's host name
+ $listinfo_uri - the URI to the list's information page.
+ $list_requests - the address to the list's `-request` address.
+ $description - the mailing list's description
+ $info - additional mailing list's information
+
+ Personalized messages will also have these placeholders available:
+
+ $user_name - the name of the subscribing user.
+ $user_address - the email address of the subscribing user.
+ $user_options_uri - the URI to this member's options page.
+ """
+ )
+
+ digest_header_uri = Attribute(
+ """URI for the header decorator on digest messages.
+
+ This can be any URI supported by `urllib2` with the addition of
+ `mailman:` URIs, which reference internal default resources. This is
+ a template which can include the following placeholders:
+
+ $listname - the FQDN list name for this mailing list.
+ $language - the language code, usually the list's preferred language.
+
+ The resource will be downloaded and cached whenever the decorator is
+ needed. The resource at this URI can contain the following
+ placeholders, which are also filled in through values on the mailing
+ list:
+
+ $fqdn_listname - the FQDN list name for this mailing list.
+ $list_name - the human readable name for the mailing list.
+ $host_name - the mailing list's host name
+ $listinfo_uri - the URI to the list's information page.
+ $list_requests - the address to the list's `-request` address.
+ $description - the mailing list's description
+ $info - additional mailing list's information
+
+ Personalized messages will also have these placeholders available:
+
+ $user_name - the name of the subscribing user.
+ $user_address - the email address of the subscribing user.
+ $user_options_uri - the URI to this member's options page.
+ """
+ )
+
+ digest_footer_uri = Attribute(
+ """URI for the footer decorator on digest messages.
+
+ This can be any URI supported by `urllib2` with the addition of
+ `mailman:` URIs, which reference internal default resources. This is
+ a template which can include the following placeholders:
+
+ $listname - the FQDN list name for this mailing list.
+ $language - the language code, usually the list's preferred language.
+
+ The resource will be downloaded and cached whenever the decorator is
+ needed. The resource at this URI can contain the following
+ placeholders, which are also filled in through values on the mailing
+ list:
+
+ $fqdn_listname - the FQDN list name for this mailing list.
+ $list_name - the human readable name for the mailing list.
+ $host_name - the mailing list's host name
+ $listinfo_uri - the URI to the list's information page.
+ $list_requests - the address to the list's `-request` address.
+ $description - the mailing list's description
+ $info - additional mailing list's information
+
+ Personalized messages will also have these placeholders available:
+
+ $user_name - the name of the subscribing user.
+ $user_address - the email address of the subscribing user.
+ $user_options_uri - the URI to this member's options page.
+ """
+ )
+
class IAcceptableAlias(Interface):
diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py
index fd7410465..16596e6d2 100644
--- a/src/mailman/model/mailinglist.py
+++ b/src/mailman/model/mailinglist.py
@@ -132,8 +132,8 @@ class MailingList(Model):
default_member_action = Enum(Action)
default_nonmember_action = Enum(Action)
description = Unicode()
- digest_footer = Unicode()
- digest_header = Unicode()
+ digest_footer_uri = Unicode()
+ digest_header_uri = Unicode()
digest_is_default = Bool()
digest_send_periodic = Bool()
digest_size_threshold = Float()
@@ -143,12 +143,14 @@ class MailingList(Model):
emergency = Bool()
encode_ascii_prefixes = Bool()
first_strip_reply_to = Bool()
+ footer_uri = Unicode()
forward_auto_discards = Bool()
gateway_to_mail = Bool()
gateway_to_news = Bool()
generic_nonmember_action = Int()
goodbye_message_uri = Unicode()
header_matches = Pickle()
+ header_uri = Unicode()
hold_these_nonmembers = Pickle()
info = Unicode()
linked_newsgroup = Unicode()
@@ -158,8 +160,6 @@ class MailingList(Model):
member_moderation_notice = Unicode()
mime_is_default_digest = Bool()
moderator_password = Unicode()
- msg_footer = Unicode()
- msg_header = Unicode()
new_member_options = Int()
news_moderation = Enum(NewsModeration)
news_prefix_subject_too = Bool()
diff --git a/src/mailman/mta/docs/decorating.rst b/src/mailman/mta/docs/decorating.rst
index 05196eb78..44559edb3 100644
--- a/src/mailman/mta/docs/decorating.rst
+++ b/src/mailman/mta/docs/decorating.rst
@@ -21,20 +21,39 @@ Decorations
Decorations are added when the mailing list had a header and/or footer
defined, and the decoration handler is told to do personalized decorations.
+We start by writing the site-global header and footer template.
::
- >>> mlist = create_list('test@example.com')
- >>> mlist.msg_header = """\
+ >>> import os, tempfile
+ >>> template_dir = tempfile.mkdtemp()
+ >>> site_dir = os.path.join(template_dir, 'site', 'en')
+ >>> os.makedirs(site_dir)
+ >>> config.push('templates', """
+ ... [paths.testing]
+ ... template_dir: {0}
+ ... """.format(template_dir))
+
+ >>> myheader_path = os.path.join(site_dir, 'myheader.txt')
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, """\
... Delivery address: $user_address
... Subscribed address: $user_delivered_to
... """
-
- >>> mlist.msg_footer = """\
+ >>> myfooter_path = os.path.join(site_dir, 'myfooter.txt')
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, """\
... User name: $user_name
... Password: $user_password
... Language: $user_language
... Options: $user_optionsurl
... """
+
+Then create a mailing list which will use this header and footer. Because
+these are site-global templates, we can use a shorted URL.
+
+ >>> mlist = create_list('test@example.com')
+ >>> mlist.header_uri = 'mailman:///myheader.txt'
+ >>> mlist.footer_uri = 'mailman:///myfooter.txt'
>>> transaction.commit()
@@ -201,3 +220,9 @@ into the message metadata.
<BLANKLINE>
This is a test.
----------
+
+.. Clean up
+
+ >>> config.pop('templates')
+ >>> import shutil
+ >>> shutil.rmtree(template_dir)
diff --git a/src/mailman/pipeline/decorate.py b/src/mailman/pipeline/decorate.py
index e7d011f84..a8fc340f8 100644
--- a/src/mailman/pipeline/decorate.py
+++ b/src/mailman/pipeline/decorate.py
@@ -29,12 +29,14 @@ import re
import logging
from email.mime.text import MIMEText
+from urllib2 import URLError
from zope.component import getUtility
from zope.interface import implements
from mailman.core.i18n import _
from mailman.email.message import Message
from mailman.interfaces.handler import IHandler
+from mailman.interfaces.templates import ITemplateLoader
from mailman.interfaces.usermanager import IUserManager
from mailman.utilities.string import expand
@@ -66,8 +68,16 @@ def process(mlist, msg, msgdata):
d['user_optionsurl'] = member.options_url
# These strings are descriptive for the log file and shouldn't be i18n'd
d.update(msgdata.get('decoration-data', {}))
- header = decorate(mlist, mlist.msg_header, d)
- footer = decorate(mlist, mlist.msg_footer, d)
+ try:
+ header = decorate(mlist, mlist.header_uri, d)
+ except URLError:
+ log.exception('Header decorator URI not found ({0}): {1}'.format(
+ mlist.fqdn_listname, mlist.header_uri))
+ try:
+ footer = decorate(mlist, mlist.footer_uri, d)
+ except URLError:
+ log.exception('Footer decorator URI not found ({0}): {1}'.format(
+ mlist.fqdn_listname, mlist.footer_uri))
# Escape hatch if both the footer and header are empty
if not header and not footer:
return
@@ -195,19 +205,28 @@ def process(mlist, msg, msgdata):
-def decorate(mlist, template, extradict=None):
+def decorate(mlist, uri, extradict=None):
"""Expand the decoration template."""
+ if uri is None:
+ return ''
+ # Get the decorator template.
+ loader = getUtility(ITemplateLoader)
+ template_uri = expand(uri, dict(
+ listname=mlist.fqdn_listname,
+ language=mlist.preferred_language.code,
+ ))
+ template = loader.get(template_uri)
# Create a dictionary which includes the default set of interpolation
# variables allowed in headers and footers. These will be augmented by
# any key/value pairs in the extradict.
substitutions = dict(
- real_name = mlist.real_name,
- list_name = mlist.list_name,
- fqdn_listname = mlist.fqdn_listname,
- host_name = mlist.mail_host,
- listinfo_page = mlist.script_url('listinfo'),
- description = mlist.description,
- info = mlist.info,
+ fqdn_listname = mlist.fqdn_listname,
+ list_name = mlist.real_name,
+ host_name = mlist.mail_host,
+ listinfo_uri = mlist.script_url('listinfo'),
+ list_requests = mlist.request_address,
+ description = mlist.description,
+ info = mlist.info,
)
if extradict is not None:
substitutions.update(extradict)
diff --git a/src/mailman/pipeline/docs/decorate.rst b/src/mailman/pipeline/docs/decorate.rst
index 1c94cff1e..e24e1e252 100644
--- a/src/mailman/pipeline/docs/decorate.rst
+++ b/src/mailman/pipeline/docs/decorate.rst
@@ -36,19 +36,42 @@ decorations are added for digest messages.
Here is a message.
-Decorating simple text messages
-===============================
+Simple decorations
+==================
-Text messages that have no declared content type character set are by default,
-encoded in us-ascii. When the mailing list's preferred language is ``en``
-(i.e. English), the character set of the mailing list and of the message will
-match. In this case, and when the header and footer have no interpolation
-placeholder variables, the message's payload will be prepended by the verbatim
-header, and appended with the verbatim footer.
+Message decorations are specified by URI and can be specialized by the mailing
+list and language. Internal Mailman decorations can be referenced by using
+the ``mailman://`` URL scheme. Here we create a simple English header and
+footer for all mailing lists in our site.
+::
+
+ >>> import os, tempfile
+ >>> template_dir = tempfile.mkdtemp()
+ >>> site_dir = os.path.join(template_dir, 'site', 'en')
+ >>> os.makedirs(site_dir)
+ >>> config.push('templates', """
+ ... [paths.testing]
+ ... template_dir: {0}
+ ... """.format(template_dir))
+
+ >>> myheader_path = os.path.join(site_dir, 'myheader.txt')
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, 'header'
+ >>> myfooter_path = os.path.join(site_dir, 'myfooter.txt')
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, 'footer'
+
+Setting these attributes on the mailing list causes it to use these
+templates. Since these are site-global templates, we can use a shorter path.
+
+ >>> mlist.header_uri = 'mailman:///myheader.txt'
+ >>> mlist.footer_uri = 'mailman:///myfooter.txt'
+
+Text messages that have no declared content type are, by default encoded in
+ASCII. When the mailing list's preferred language is ``en`` (i.e. English),
+the character set of the mailing list and of the message will match, allowing
+Mailman to simply prepend the header and append the footer verbatim.
- >>> msg = message_from_string(msg_text)
- >>> mlist.msg_header = 'header\n'
- >>> mlist.msg_footer = 'footer'
>>> mlist.preferred_language = 'en'
>>> process(mlist, msg, {})
>>> print msg.as_string()
@@ -63,10 +86,14 @@ Mailman supports a number of interpolation variables, placeholders in the
header and footer for information to be filled in with mailing list specific
data. An example of such information is the mailing list's `real name` (a
short descriptive name for the mailing list).
+::
+
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, '$list_name header'
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, '$list_name footer'
>>> msg = message_from_string(msg_text)
- >>> mlist.msg_header = '$real_name header\n'
- >>> mlist.msg_footer = '$real_name footer'
>>> mlist.real_name = 'XTest'
>>> process(mlist, msg, {})
>>> print msg.as_string()
@@ -78,10 +105,14 @@ short descriptive name for the mailing list).
You can't just pick any interpolation variable though; if you do, the variable
will remain in the header or footer unchanged.
+::
+
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, '$dummy header'
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, '$dummy footer'
>>> msg = message_from_string(msg_text)
- >>> mlist.msg_header = '$dummy header\n'
- >>> mlist.msg_footer = '$dummy footer'
>>> process(mlist, msg, {})
>>> print msg.as_string()
From: aperson@example.org
@@ -103,14 +134,18 @@ display in a proportional font.
When Mailman sees text/plain messages with such RFC 3676 parameters, it
preserves these parameters when it concatenates headers and footers to the
message payload.
+::
+
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, 'header'
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, 'footer'
- >>> mlist.msg_header = 'header'
- >>> mlist.msg_footer = 'footer'
>>> mlist.preferred_language = 'en'
>>> msg = message_from_string("""\
... From: aperson@example.org
... Content-Type: text/plain; format=flowed; delsp=no
- ...
+ ...
... Here is a message\x20
... with soft line breaks.
... """)
@@ -120,14 +155,18 @@ message payload.
>>> # message' line will be retained in the output.
>>> print msg['content-type']
text/plain; format="flowed"; delsp="no"; charset="us-ascii"
- >>> [line for line in msg.get_payload().splitlines()]
- ['header', 'Here is a message ', 'with soft line breaks.', 'footer']
+ >>> for line in msg.get_payload().splitlines():
+ ... print '>{0}<'.format(line)
+ >header<
+ >Here is a message <
+ >with soft line breaks.<
+ >footer<
Decorating mixed-charset messages
=================================
-When a message has no explicit character set, it is assumed to be us-ascii.
+When a message has no explicit character set, it is assumed to be ASCII.
However, if the mailing list's preferred language has a different character
set, Mailman will still try to concatenate the header and footer, but it will
convert the text to utf-8 and base-64 encode the message payload.
@@ -135,8 +174,11 @@ convert the text to utf-8 and base-64 encode the message payload.
# 'ja' = Japanese; charset = 'euc-jp'
>>> mlist.preferred_language = 'ja'
- >>> mlist.msg_header = '$description header'
- >>> mlist.msg_footer = '$description footer'
+
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, '$description header'
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, '$description footer'
>>> mlist.description = '\u65e5\u672c\u8a9e'
>>> from email.message import Message
@@ -154,15 +196,19 @@ convert the text to utf-8 and base-64 encode the message payload.
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
<BLANKLINE>
- 5pel5pys6KqeIGhlYWRlcgpGcmFuw6dhaXNlCuaXpeacrOiqniBmb290ZXI=
+ 5pel5pys6KqeIGhlYWRlcgpGcmFuw6dhaXNlCuaXpeacrOiqniBmb290ZXIK
Sometimes the message even has an unknown character set. In this case,
Mailman has no choice but to decorate the original message with MIME
attachments.
+::
>>> mlist.preferred_language = 'en'
- >>> mlist.msg_header = 'header'
- >>> mlist.msg_footer = 'footer'
+ >>> with open(myheader_path, 'w') as fp:
+ ... print >> fp, 'header'
+ >>> with open(myfooter_path, 'w') as fp:
+ ... print >> fp, 'footer'
+
>>> msg = message_from_string("""\
... From: aperson@example.org
... Content-Type: text/plain; charset=unknown
@@ -170,6 +216,7 @@ attachments.
...
... Here is a message.
... """)
+
>>> process(mlist, msg, {})
>>> msg.set_boundary('BOUNDARY')
>>> print msg.as_string()
@@ -211,9 +258,6 @@ When the outer part is ``multipart/mixed``, the header and footer can have a
``Content-Disposition`` of ``inline`` so that MUAs can display these headers
as if they were simply concatenated.
- >>> mlist.preferred_language = 'en'
- >>> mlist.msg_header = 'header'
- >>> mlist.msg_footer = 'footer'
>>> part_1 = message_from_string("""\
... From: aperson@example.org
...
@@ -296,3 +340,9 @@ so that the header and footer can be added as attachments.
<BLANKLINE>
footer
--BOUNDARY--
+
+.. Clean up
+
+ >>> config.pop('templates')
+ >>> import shutil
+ >>> shutil.rmtree(template_dir)
diff --git a/src/mailman/runners/digest.py b/src/mailman/runners/digest.py
index 82895be98..2730fc427 100644
--- a/src/mailman/runners/digest.py
+++ b/src/mailman/runners/digest.py
@@ -26,6 +26,7 @@ __all__ = [
import re
+import logging
# cStringIO doesn't support unicode.
from StringIO import StringIO
@@ -37,6 +38,7 @@ from email.mime.message import MIMEMessage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate, getaddresses, make_msgid
+from urllib2 import URLError
from mailman.config import config
from mailman.core.errors import DiscardMessage
@@ -50,6 +52,9 @@ from mailman.utilities.mailbox import Mailbox
from mailman.utilities.string import oneline, wrap
+log = logging.getLogger('mailman.error')
+
+
class Digester:
"""Base digester class."""
@@ -85,7 +90,14 @@ class Digester:
got_owner_email=mlist.owner_address,
)
# Set things up for the table of contents.
- self._header = decorate(mlist, mlist.digest_header)
+ if mlist.digest_header_uri is not None:
+ try:
+ self._header = decorate(mlist, mlist.digest_header_uri)
+ except URLError:
+ log.exception(
+ 'Digest header decorator URI not found ({0}): {1}'.format(
+ mlist.fqdn_listname, mlist.digest_header_uri))
+ self._header = ''
self._toc = StringIO()
print >> self._toc, _("Today's Topics:\n")
@@ -144,6 +156,7 @@ class Digester:
+
class MIMEDigester(Digester):
"""A MIME digester."""
@@ -154,7 +167,7 @@ class MIMEDigester(Digester):
masthead['Content-Description'] = self._subject
self._message.attach(masthead)
# Add the optional digest header.
- if mlist.digest_header:
+ if mlist.digest_header_uri is not None:
header = MIMEText(self._header.encode(self._charset),
_charset=self._charset)
header['Content-Description'] = _('Digest Header')
@@ -184,8 +197,16 @@ class MIMEDigester(Digester):
def finish(self):
"""Finish up the digest, producing the email-ready copy."""
- if self._mlist.digest_footer:
- footer_text = decorate(self._mlist, self._mlist.digest_footer)
+ if self._mlist.digest_footer_uri is not None:
+ try:
+ footer_text = decorate(
+ self._mlist, self._mlist.digest_footer_uri)
+ except URLError:
+ log.exception(
+ 'Digest footer decorator URI not found ({0}): {1}'.format(
+ self._mlist.fqdn_listname,
+ self._mlist.digest_footer_uri))
+ footer_text = ''
footer = MIMEText(footer_text.encode(self._charset),
_charset=self._charset)
footer['Content-Description'] = _('Digest Footer')
@@ -211,7 +232,7 @@ class RFC1153Digester(Digester):
print >> self._text, self._masthead
print >> self._text
# Add the optional digest header.
- if mlist.digest_header:
+ if mlist.digest_header_uri is not None:
print >> self._text, self._header
print >> self._text
# Calculate the set of headers we're to keep in the RFC1153 digest.
@@ -262,8 +283,16 @@ class RFC1153Digester(Digester):
def finish(self):
"""Finish up the digest, producing the email-ready copy."""
- if self._mlist.digest_footer:
- footer_text = decorate(self._mlist, self._mlist.digest_footer)
+ if self._mlist.digest_footer_uri is not None:
+ try:
+ footer_text = decorate(
+ self._mlist, self._mlist.digest_footer_uri)
+ except URLError:
+ log.exception(
+ 'Digest footer decorator URI not found ({0}): {1}'.format(
+ self._mlist.fqdn_listname,
+ self._mlist.digest_footer_uri))
+ footer_text = ''
# This is not strictly conformant RFC 1153. The trailer is only
# supposed to contain two lines, i.e. the "End of ... Digest" line
# and the row of asterisks. If this screws up MUAs, the solution
diff --git a/src/mailman/styles/default.py b/src/mailman/styles/default.py
index 8d4ef83df..623bf1bc1 100644
--- a/src/mailman/styles/default.py
+++ b/src/mailman/styles/default.py
@@ -106,13 +106,9 @@ from: .*@uplinkpro.com
mlist.mime_is_default_digest = False
mlist.digest_size_threshold = 30 # KB
mlist.digest_send_periodic = True
- mlist.digest_header = ''
- mlist.digest_footer = """\
-_______________________________________________
-$real_name mailing list
-$fqdn_listname
-${listinfo_page}
-"""
+ mlist.digest_header_uri = None
+ mlist.digest_footer_uri = (
+ 'mailman:///$listname/$language/footer-generic.txt')
mlist.digest_volume_frequency = DigestFrequency.monthly
mlist.next_digest_number = 1
mlist.nondigestable = True
@@ -137,13 +133,8 @@ ${listinfo_page}
# 2-tuple of the date of the last autoresponse and the number of
# autoresponses sent on that date.
mlist.subject_prefix = _('[$mlist.real_name] ')
- mlist.msg_header = ''
- mlist.msg_footer = """\
-_______________________________________________
-$real_name mailing list
-$fqdn_listname
-${listinfo_page}
-"""
+ mlist.header_uri = None
+ mlist.footer_uri = 'mailman:///$listname/$language/footer-generic.txt'
# Set this to Never if the list's preferred language uses us-ascii,
# otherwise set it to As Needed.
if mlist.preferred_language.charset == 'us-ascii':
diff --git a/src/mailman/templates/en/footer-generic.txt b/src/mailman/templates/en/footer-generic.txt
new file mode 100644
index 000000000..ef76e4986
--- /dev/null
+++ b/src/mailman/templates/en/footer-generic.txt
@@ -0,0 +1,4 @@
+_______________________________________________
+$list_name mailing list
+$fqdn_listname
+${listinfo_uri}