Application level moderation
At an application level, moderation involves holding messages and membership changes for moderator approval. This utilizes the lower level interface for list-centric moderation requests.
Moderation is always mailing list-centric.
>>> from mailman.app.lifecycle import create_list
>>> mlist = create_list('ant@example.com')
>>> mlist.preferred_language = 'en'
>>> mlist.display_name = 'A Test List'
>>> mlist.admin_immed_notify = False
We’ll use the lower level API for diagnostic purposes.
>>> from mailman.interfaces.requests import IListRequests
>>> requests = IListRequests(mlist)
Message moderation
Holding messages
Anne posts a message to the mailing list, but she is not a member of the list, so the message is held for moderator approval.
>>> from mailman.testing.helpers import (specialized_message_from_string
... as message_from_string)
>>> msg = message_from_string("""\
... From: anne@example.org
... To: ant@example.com
... Subject: Something important
... Message-ID: <aardvark>
...
... Here's something important about our mailing list.
... """)
Holding a message means keeping a copy of it that a moderator must approve before the message is posted to the mailing list. To hold the message, the message, its metadata, and a reason for the hold must be provided. In this case, we won’t include any additional metadata.
>>> from mailman.app.moderator import hold_message
>>> hold_message(mlist, msg, {}, 'Needs approval')
1
We can also hold a message with some additional metadata.
>>> msg = message_from_string("""\
... From: bart@example.org
... To: ant@example.com
... Subject: Something important
... Message-ID: <badger>
...
... Here's something important about our mailing list.
... """)
>>> msgdata = dict(sender='anne@example.com', approved=True)
>>> hold_message(mlist, msg, msgdata, 'Feeling ornery')
2
Disposing of messages
The moderator can select one of several dispositions:
discard - throw the message away.
reject - bounces the message back to the original author.
defer - defer any action on the message (continue to hold it)
accept - accept the message for posting.
The most trivial is to simply defer a decision for now.
>>> from mailman.interfaces.action import Action
>>> from mailman.app.moderator import handle_message
>>> handle_message(mlist, 1, Action.defer)
This leaves the message in the requests database.
>>> key, data = requests.get_request(1)
>>> print(key)
<aardvark>
The moderator can also discard the message.
>>> handle_message(mlist, 1, Action.discard)
>>> print(requests.get_request(1))
None
The message can be rejected, which bounces the message back to the original sender.
>>> handle_message(mlist, 2, Action.reject, 'Off topic')
The message is no longer available in the requests database.
>>> print(requests.get_request(2))
None
And there is one message in the virgin queue - the rejection notice.
>>> from mailman.testing.helpers import get_queue_messages
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
>>> print(messages[0].msg.as_string())
MIME-Version: 1.0
...
Subject: Request to mailing list "A Test List" rejected
From: ant-bounces@example.com
To: bart@example.org
...
Your request to the ant@example.com mailing list
Posting of your message titled "Something important"
has been rejected by the list moderator. The moderator gave the
following reason for rejecting your request:
"Off topic"
Any questions or comments should be directed to the list administrator
at:
ant-owner@example.com
The bounce gets sent to the original sender.
>>> for recipient in sorted(messages[0].msgdata['recipients']):
... print(recipient)
bart@example.org
Or the message can be approved.
>>> msg = message_from_string("""\
... From: cris@example.org
... To: ant@example.com
... Subject: Something important
... Message-ID: <caribou>
...
... Here's something important about our mailing list.
... """)
>>> id = hold_message(mlist, msg, {}, 'Needs approval')
>>> handle_message(mlist, id, Action.accept)
This places the message back into the incoming queue for further processing, however the message metadata indicates that the message has been approved.
>>> messages = get_queue_messages('pipeline')
>>> len(messages)
1
>>> print(messages[0].msg.as_string())
From: cris@example.org
To: ant@example.com
Subject: Something important
...
>>> from mailman.testing.documentation import dump_msgdata
>>> dump_msgdata(messages[0].msgdata)
_parsemsg : False
approved : True
moderator_approved: True
type : data
version : 3
Forwarding the message
The message can be forwarded to another address. This is helpful for getting the message into the inbox of one of the moderators.
>>> msg = message_from_string("""\
... From: elly@example.org
... To: ant@example.com
... Subject: Something important
... Message-ID: <elephant>
...
... Here's something important about our mailing list.
... """)
>>> req_id = hold_message(mlist, msg, {}, 'Needs approval')
>>> handle_message(mlist, req_id, Action.discard,
... forward=['zack@example.com'])
The forwarded message is in the virgin queue, destined for the moderator.
>>> messages = get_queue_messages('virgin')
>>> len(messages)
1
>>> print(messages[0].msg.as_string())
Subject: Forward of moderated message
From: ant-bounces@example.com
To: zack@example.com
...
>>> for recipient in sorted(messages[0].msgdata['recipients']):
... print(recipient)
zack@example.com
Holding unsubscription requests
Some lists require moderator approval for unsubscriptions. In this case, only the unsubscribing address is required.
Fred is a member of the mailing list…
>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
>>> mlist.send_welcome_message = False
>>> fred = getUtility(IUserManager).create_address(
... 'fred@example.com', 'Fred Person')
>>> from mailman.interfaces.subscriptions import ISubscriptionManager
>>> registrar = ISubscriptionManager(mlist)
>>> token, token_owner, member = registrar.register(
... fred, pre_verified=True, pre_confirmed=True, pre_approved=True)
>>> member
<Member: Fred Person <fred@example.com> on ant@example.com
as MemberRole.member>
…but now that he wants to leave the mailing list, his request must be approved.
>>> from mailman.app.moderator import hold_unsubscription
>>> req_id = hold_unsubscription(mlist, 'fred@example.com')
As with subscription requests, the unsubscription request can be deferred.
>>> from mailman.app.moderator import handle_unsubscription
>>> handle_unsubscription(mlist, req_id, Action.defer)
>>> print(mlist.members.get_member('fred@example.com').address)
Fred Person <fred@example.com>
The held unsubscription can also be discarded, and the member will remain subscribed.
>>> handle_unsubscription(mlist, req_id, Action.discard)
>>> print(mlist.members.get_member('fred@example.com').address)
Fred Person <fred@example.com>
The request can be rejected, in which case a message is sent to the member, and the person remains a member of the mailing list.
>>> req_id = hold_unsubscription(mlist, 'fred@example.com')
>>> handle_unsubscription(mlist, req_id, Action.reject, 'No can do')
>>> mlist.members.get_member('fred@example.com').address, req_id = hold_unsubscription(mlist, 'fred@example.com')
As ree-l2">)