summaryrefslogtreecommitdiff
path: root/src/mailman/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/handlers')
-rw-r--r--src/mailman/handlers/acknowledge.py24
-rw-r--r--src/mailman/handlers/decorate.py63
-rw-r--r--src/mailman/handlers/docs/acknowledge.rst8
-rw-r--r--src/mailman/handlers/docs/decorate.rst26
-rw-r--r--src/mailman/handlers/docs/rfc-2369.rst24
-rw-r--r--src/mailman/handlers/replybot.py3
-rw-r--r--src/mailman/handlers/rfc_2369.py7
-rw-r--r--src/mailman/handlers/tests/test_decorate.py16
-rw-r--r--src/mailman/handlers/tests/test_rfc_2369.py7
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