summaryrefslogtreecommitdiff
path: root/src/mailman/pipeline
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/pipeline')
-rw-r--r--src/mailman/pipeline/decorate.py39
-rw-r--r--src/mailman/pipeline/docs/decorate.rst108
2 files changed, 108 insertions, 39 deletions
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)