diff options
Diffstat (limited to 'src/mailman/handlers')
| -rw-r--r-- | src/mailman/handlers/acknowledge.py | 24 | ||||
| -rw-r--r-- | src/mailman/handlers/decorate.py | 63 | ||||
| -rw-r--r-- | src/mailman/handlers/docs/acknowledge.rst | 8 | ||||
| -rw-r--r-- | src/mailman/handlers/docs/decorate.rst | 26 | ||||
| -rw-r--r-- | src/mailman/handlers/docs/rfc-2369.rst | 24 | ||||
| -rw-r--r-- | src/mailman/handlers/replybot.py | 3 | ||||
| -rw-r--r-- | src/mailman/handlers/rfc_2369.py | 7 | ||||
| -rw-r--r-- | src/mailman/handlers/tests/test_decorate.py | 16 | ||||
| -rw-r--r-- | src/mailman/handlers/tests/test_rfc_2369.py | 7 |
9 files changed, 76 insertions, 102 deletions
diff --git a/src/mailman/handlers/acknowledge.py b/src/mailman/handlers/acknowledge.py index 3acb47916..f493c4aa6 100644 --- a/src/mailman/handlers/acknowledge.py +++ b/src/mailman/handlers/acknowledge.py @@ -25,8 +25,8 @@ from mailman.core.i18n import _ from mailman.email.message import UserNotification from mailman.interfaces.handler import IHandler from mailman.interfaces.languages import ILanguageManager -from mailman.utilities.i18n import make -from mailman.utilities.string import oneline +from mailman.interfaces.template import ITemplateLoader +from mailman.utilities.string import expand, oneline from zope.component import getUtility from zope.interface import implementer @@ -60,17 +60,15 @@ class Acknowledge: if 'lang' in msgdata else member.preferred_language) # Now get the acknowledgement template. - display_name = mlist.display_name - text = make('postack.txt', - mlist=mlist, - language=language.code, - wrap=False, - subject=oneline(original_subject, in_unicode=True), - list_name=mlist.list_name, - display_name=display_name, - listinfo_url=mlist.script_url('listinfo'), - optionsurl=member.options_url, - ) + display_name = mlist.display_name # noqa + template = getUtility(ITemplateLoader).get( + 'list:user:notice:post', mlist, + language=language.code) + text = expand(template, mlist, dict( + subject=oneline(original_subject, in_unicode=True), + # For backward compatibility. + list_name=mlist.list_name, + )) # Craft the outgoing message, with all headers and attributes # necessary for general delivery. Then enqueue it to the outgoing # queue. diff --git a/src/mailman/handlers/decorate.py b/src/mailman/handlers/decorate.py index 186c86a6d..7f5519fd0 100644 --- a/src/mailman/handlers/decorate.py +++ b/src/mailman/handlers/decorate.py @@ -21,14 +21,14 @@ import re import logging from email.mime.text import MIMEText +from email.utils import formataddr from mailman import public from mailman.core.i18n import _ from mailman.email.message import Message from mailman.interfaces.handler import IHandler from mailman.interfaces.mailinglist import IListArchiverSet -from mailman.interfaces.templates import ITemplateLoader +from mailman.interfaces.template import ITemplateLoader from mailman.utilities.string import expand -from urllib.error import URLError from zope.component import getUtility from zope.interface import implementer @@ -47,13 +47,14 @@ def process(mlist, msg, msgdata): if member is not None: # Calculate the extra personalization dictionary. recipient = msgdata.get('recipient', member.address.original_email) - d['user_address'] = recipient + d['member'] = formataddr( + (member.subscriber.display_name, member.subscriber.email)) + d['user_email'] = recipient d['user_delivered_to'] = member.address.original_email d['user_language'] = member.preferred_language.description - d['user_name'] = (member.user.display_name - if member.user.display_name - else member.address.original_email) - d['user_optionsurl'] = member.options_url + d['user_name'] = member.display_name + # For backward compatibility. + d['user_address'] = recipient # Calculate the archiver permalink substitution variables. This provides # the $<archive-name>_url placeholder for every enabled archiver. for archiver in IListArchiverSet(mlist).archivers: @@ -71,20 +72,10 @@ def process(mlist, msg, msgdata): d[placeholder] = archive_url # These strings are descriptive for the log file and shouldn't be i18n'd d.update(msgdata.get('decoration-data', {})) - try: - header = decorate(mlist, mlist.header_uri, d) - except URLError: - header = None - 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: - footer = None - log.exception('Footer decorator URI not found ({0}): {1}'.format( - mlist.fqdn_listname, mlist.footer_uri)) + header = decorate('list:member:regular:header', mlist, d) + footer = decorate('list:member:regular:footer', mlist, d) # Escape hatch if both the footer and header are empty or None. - if not header and not footer: + if len(header) == 0 and len(footer) == 0: return # Be MIME smart here. We only attach the header and footer by # concatenation when the message is a non-multipart of type text/plain. @@ -120,9 +111,9 @@ def process(mlist, msg, msgdata): oldpayload = msg.get_payload(decode=True).decode(mcset) del msg['content-transfer-encoding'] frontsep = endsep = '' - if header and not header.endswith('\n'): + if len(header) > 0 and not header.endswith('\n'): frontsep = '\n' - if footer and not oldpayload.endswith('\n'): + if len(footer) > 0 and not oldpayload.endswith('\n'): endsep = '\n' payload = header + frontsep + oldpayload + endsep + footer # When setting the payload for the message, try various charset @@ -152,11 +143,11 @@ def process(mlist, msg, msgdata): payload = msg.get_payload() if not isinstance(payload, list): payload = [payload] - if footer: + if len(footer) > 0: mimeftr = MIMEText(footer.encode(lcset), 'plain', lcset) mimeftr['Content-Disposition'] = 'inline' payload.append(mimeftr) - if header: + if len(header) > 0: mimehdr = MIMEText(header.encode(lcset), 'plain', lcset) mimehdr['Content-Disposition'] = 'inline' payload.insert(0, mimehdr) @@ -194,11 +185,11 @@ def process(mlist, msg, msgdata): # subparts: the header (if any), the wrapped message, and the footer (if # any). payload = [inner] - if header: + if len(header) > 0: mimehdr = MIMEText(header.encode(lcset), 'plain', lcset) mimehdr['Content-Disposition'] = 'inline' payload.insert(0, mimehdr) - if footer: + if len(footer) > 0: mimeftr = MIMEText(footer.encode(lcset), 'plain', lcset) mimeftr['Content-Disposition'] = 'inline' payload.append(mimeftr) @@ -210,18 +201,12 @@ def process(mlist, msg, msgdata): @public -def decorate(mlist, uri, extradict=None): - """Expand the decoration template from its URI.""" - if uri is None: - return '' +def decorate(name, mlist, extradict=None): + """Expand the named decoration template uri.""" + if extradict is None: + extradict = {} # Get the decorator template. - loader = getUtility(ITemplateLoader) - template_uri = expand(uri, dict( - language=mlist.preferred_language.code, - list_id=mlist.list_id, - listname=mlist.fqdn_listname, - )) - template = loader.get(template_uri) + template = getUtility(ITemplateLoader).get(name, mlist, **extradict) return decorate_template(mlist, template, extradict) @@ -242,11 +227,9 @@ def decorate_template(mlist, template, extradict=None): 'info', ) } - # This must eventually go away. - substitutions['listinfo_uri'] = mlist.script_url('listinfo') if extradict is not None: substitutions.update(extradict) - text = expand(template, substitutions) + text = expand(template, mlist, substitutions) # Turn any \r\n line endings into just \n return re.sub(r' *\r?\n', r'\n', text) diff --git a/src/mailman/handlers/docs/acknowledge.rst b/src/mailman/handlers/docs/acknowledge.rst index 42cab04a0..fbe46b063 100644 --- a/src/mailman/handlers/docs/acknowledge.rst +++ b/src/mailman/handlers/docs/acknowledge.rst @@ -133,10 +133,6 @@ The receipt will include the original message's subject in the response body, Something witty and insightful <BLANKLINE> was successfully received by the Test mailing list. - <BLANKLINE> - List info page: http://lists.example.com/listinfo/test@example.com - Your preferences: http://example.com/aperson@example.com - <BLANKLINE> If there is no subject, then the receipt will use a generic message. @@ -169,7 +165,3 @@ If there is no subject, then the receipt will use a generic message. (no subject) <BLANKLINE> was successfully received by the Test mailing list. - <BLANKLINE> - List info page: http://lists.example.com/listinfo/test@example.com - Your preferences: http://example.com/aperson@example.com - <BLANKLINE> diff --git a/src/mailman/handlers/docs/decorate.rst b/src/mailman/handlers/docs/decorate.rst index e6199f8e0..7e02ea9eb 100644 --- a/src/mailman/handlers/docs/decorate.rst +++ b/src/mailman/handlers/docs/decorate.rst @@ -6,7 +6,7 @@ Message decoration is the process of adding headers and footers to the original message. A handler module takes care of this based on the settings of the mailing list and the type of message being processed. - >>> mlist = create_list('_xtest@example.com') + >>> mlist = create_list('ant@example.com') >>> msg_text = """\ ... From: aperson@example.org ... @@ -41,7 +41,7 @@ Simple decorations 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 +the ``mailman:///`` URL scheme. Here we create a simple English header and footer for all mailing lists in our site. :: @@ -51,7 +51,7 @@ footer for all mailing lists in our site. >>> os.makedirs(site_dir) >>> config.push('templates', """ ... [paths.testing] - ... template_dir: {0} + ... template_dir: {} ... """.format(template_dir)) >>> myheader_path = os.path.join(site_dir, 'myheader.txt') @@ -61,11 +61,17 @@ footer for all mailing lists in our site. >>> with open(myfooter_path, 'w') as fp: ... print('footer', file=fp) -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. +Adding these template URIs to the template manager sets the mailing list up 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' + >>> from mailman.interfaces.template import ITemplateManager + >>> from zope.component import getUtility + >>> manager = getUtility(ITemplateManager) + >>> manager.set('list:member:regular:header', + ... mlist.list_id, 'mailman:///myheader.txt') + >>> manager.set('list:member:regular:footer', + ... mlist.list_id, '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), @@ -94,14 +100,14 @@ short descriptive name for the mailing list). ... print('$display_name footer', file=fp) >>> msg = message_from_string(msg_text) - >>> mlist.display_name = 'XTest' + >>> mlist.display_name = 'Ant' >>> process(mlist, msg, {}) >>> print(msg.as_string()) From: aperson@example.org ... - XTest header + Ant header Here is a message. - XTest footer + Ant footer You can't just pick any interpolation variable though; if you do, the variable will remain in the header or footer unchanged. diff --git a/src/mailman/handlers/docs/rfc-2369.rst b/src/mailman/handlers/docs/rfc-2369.rst index 0f3cb5c5e..e2d2d6d47 100644 --- a/src/mailman/handlers/docs/rfc-2369.rst +++ b/src/mailman/handlers/docs/rfc-2369.rst @@ -73,10 +73,8 @@ have a reduced set of `List-` headers. Specifically, there is no `List-Post`, ---start--- list-help: <mailto:test-request@example.com?subject=help> list-id: <test.example.com> - list-subscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-join@example.com> - list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-leave@example.com> + list-subscribe: <mailto:test-join@example.com> + list-unsubscribe: <mailto:test-leave@example.com> ---end--- @@ -98,10 +96,8 @@ header which contains the `mailto:` URL used to send messages to the list. list-help: <mailto:test-request@example.com?subject=help> list-id: <test.example.com> list-post: <mailto:test@example.com> - list-subscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-join@example.com> - list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-leave@example.com> + list-subscribe: <mailto:test-join@example.com> + list-unsubscribe: <mailto:test-leave@example.com> ---end--- Some mailing lists are announce, or one-way lists, not discussion lists. @@ -120,10 +116,8 @@ to RFC 2369. list-help: <mailto:test-request@example.com?subject=help> list-id: <test.example.com> list-post: NO - list-subscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-join@example.com> - list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-leave@example.com> + list-subscribe: <mailto:test-join@example.com> + list-unsubscribe: <mailto:test-leave@example.com> ---end--- @@ -145,10 +139,8 @@ header. list-help: <mailto:test-request@example.com?subject=help> list-id: My test mailing list <test.example.com> list-post: <mailto:test@example.com> - list-subscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-join@example.com> - list-unsubscribe: <http://lists.example.com/listinfo/test@example.com>, - <mailto:test-leave@example.com> + list-subscribe: <mailto:test-join@example.com> + list-unsubscribe: <mailto:test-leave@example.com> ---end--- Any existing ``List-Id`` headers are removed from the original message. diff --git a/src/mailman/handlers/replybot.py b/src/mailman/handlers/replybot.py index 546bb1ec4..a86ccbbf3 100644 --- a/src/mailman/handlers/replybot.py +++ b/src/mailman/handlers/replybot.py @@ -100,12 +100,11 @@ class Replybot: d = dict( list_name=mlist.list_name, display_name=display_name, - listurl=mlist.script_url('listinfo'), requestemail=mlist.request_address, owneremail=mlist.owner_address, ) # Interpolation and Wrap the response text. - text = wrap(expand(response_text, d)) + text = wrap(expand(response_text, mlist, d)) outmsg = UserNotification(msg.sender, mlist.bounces_address, subject, text, mlist.preferred_language) outmsg['X-Mailer'] = _('The Mailman Replybot') diff --git a/src/mailman/handlers/rfc_2369.py b/src/mailman/handlers/rfc_2369.py index 59c618da8..e042bc5e3 100644 --- a/src/mailman/handlers/rfc_2369.py +++ b/src/mailman/handlers/rfc_2369.py @@ -57,8 +57,6 @@ def process(mlist, msg, msgdata): # "X-List-Administrivia: yes" header. For all others (i.e. those coming # from list posts), we add a bunch of other RFC 2369 headers. requestaddr = mlist.request_address - subfieldfmt = '<{}>, <mailto:{}>' - listinfo = mlist.script_url('listinfo') headers = [] # XXX reduced_list_headers used to suppress List-Help, List-Subject, and # List-Unsubscribe from UserNotification. That doesn't seem to make sense @@ -66,9 +64,8 @@ def process(mlist, msg, msgdata): # suppressed). headers.extend(( ('List-Help', '<mailto:{}?subject=help>'.format(requestaddr)), - ('List-Unsubscribe', - subfieldfmt.format(listinfo, mlist.leave_address)), - ('List-Subscribe', subfieldfmt.format(listinfo, mlist.join_address)), + ('List-Unsubscribe', '<mailto:{}>'.format(mlist.leave_address)), + ('List-Subscribe', '<mailto:{}>'.format(mlist.join_address)), )) if not msgdata.get('reduced_list_headers'): # List-Post: is controlled by a separate attribute, which is somewhat diff --git a/src/mailman/handlers/tests/test_decorate.py b/src/mailman/handlers/tests/test_decorate.py index 7d44214cb..2187a908b 100644 --- a/src/mailman/handlers/tests/test_decorate.py +++ b/src/mailman/handlers/tests/test_decorate.py @@ -24,10 +24,12 @@ from mailman.app.lifecycle import create_list from mailman.config import config from mailman.handlers import decorate from mailman.interfaces.archiver import IArchiver +from mailman.interfaces.template import ITemplateManager from mailman.testing.helpers import ( LogFileMark, specialized_message_from_string as mfs) from mailman.testing.layers import ConfigLayer from tempfile import TemporaryDirectory +from zope.component import getUtility from zope.interface import implementer @@ -86,7 +88,8 @@ This is a test message. footer_path = os.path.join(site_dir, 'myfooter.txt') with open(footer_path, 'w', encoding='utf-8') as fp: print('${testarchiver_url}', file=fp) - self._mlist.footer_uri = 'mailman:///myfooter.txt' + getUtility(ITemplateManager).set( + 'list:member:regular:footer', None, 'mailman:///myfooter.txt') self._mlist.preferred_language = 'en' decorate.process(self._mlist, self._msg, {}) self.assertIn('http://example.com/link_to_message', @@ -100,7 +103,9 @@ This is a test message. footer_path = os.path.join(list_dir, 'myfooter.txt') with open(footer_path, 'w', encoding='utf-8') as fp: print('${testarchiver_url}', file=fp) - self._mlist.footer_uri = 'mailman:///${list_id}/myfooter.txt' + getUtility(ITemplateManager).set( + 'list:member:regular:footer', self._mlist.list_id, + 'mailman:///${list_id}/myfooter.txt') self._mlist.preferred_language = 'en' decorate.process(self._mlist, self._msg, {}) self.assertIn('http://example.com/link_to_message', @@ -114,7 +119,8 @@ This is a test message. footer_path = os.path.join(list_dir, 'myfooter.txt') with open(footer_path, 'w', encoding='utf-8') as fp: print('${testarchiver_url}', file=fp) - self._mlist.footer_uri = ( + getUtility(ITemplateManager).set( + 'list:member:regular:footer', self._mlist.list_id, 'mailman:///${list_id}/${language}/myfooter.txt') self._mlist.preferred_language = 'it' decorate.process(self._mlist, self._msg, {}) @@ -155,7 +161,9 @@ This is a test message. footer_path = os.path.join(site_dir, 'myfooter.txt') with open(footer_path, 'w', encoding='utf-8') as fp: print('${broken_url}', file=fp) - self._mlist.footer_uri = 'mailman:///myfooter.txt' + getUtility(ITemplateManager).set( + 'list:member:regular:footer', self._mlist.list_id, + 'mailman:///myfooter.txt') self._mlist.preferred_language = 'en' mark = LogFileMark('mailman.archiver') decorate.process(self._mlist, self._msg, {}) diff --git a/src/mailman/handlers/tests/test_rfc_2369.py b/src/mailman/handlers/tests/test_rfc_2369.py index e78cf9c47..41b94ae0f 100644 --- a/src/mailman/handlers/tests/test_rfc_2369.py +++ b/src/mailman/handlers/tests/test_rfc_2369.py @@ -37,7 +37,7 @@ class DummyArchiver: def list_url(self, mlist): """See `IArchiver`.""" - return mlist.domain.base_url + return 'http://{}'.format(mlist.mail_host) def permalink(self, mlist, msg): """See `IArchiver`.""" @@ -105,11 +105,10 @@ Dummy text self.addCleanup(config.pop, 'archiver') rfc_2369.process(self._mlist, self._msg, {}) self.assertEqual( - self._msg.get_all('List-Archive'), - ['<http://lists.example.com>']) + self._msg.get_all('List-Archive'), ['<http://example.com>']) self.assertEqual( self._msg.get_all('Archived-At'), - ['<http://lists.example.com/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB>']) + ['<http://example.com/4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB>']) def test_prototype_no_url(self): # The prototype archiver is not web-based, it must not return URLs |
