diff options
Diffstat (limited to 'src/mailman/pipeline')
| -rw-r--r-- | src/mailman/pipeline/decorate.py | 39 | ||||
| -rw-r--r-- | src/mailman/pipeline/docs/decorate.rst | 108 |
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) |
