diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/app/docs/moderator.rst | 248 | ||||
| -rw-r--r-- | src/mailman/app/notifications.py | 45 | ||||
| -rw-r--r-- | src/mailman/docs/NEWS.rst | 8 |
3 files changed, 195 insertions, 106 deletions
diff --git a/src/mailman/app/docs/moderator.rst b/src/mailman/app/docs/moderator.rst index 22e1da666..ec48f6e88 100644 --- a/src/mailman/app/docs/moderator.rst +++ b/src/mailman/app/docs/moderator.rst @@ -13,6 +13,7 @@ Moderation is always mailing list-centric. >>> 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. @@ -74,7 +75,6 @@ The moderator can select one of several dispositions: * 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 @@ -103,7 +103,7 @@ 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 bounce. +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') @@ -141,7 +141,7 @@ The bounce gets sent to the original sender. Or the message can be approved. >>> msg = message_from_string("""\ - ... From: cate@example.org + ... From: cris@example.org ... To: ant@example.com ... Subject: Something important ... Message-ID: <caribou> @@ -159,7 +159,7 @@ however the message metadata indicates that the message has been approved. >>> len(messages) 1 >>> print messages[0].msg.as_string() - From: cate@example.org + From: cris@example.org To: ant@example.com Subject: Something important ... @@ -195,9 +195,7 @@ a copy to be preserve, which skips deleting the message from the storage. >>> from mailman.interfaces.messages import IMessageStore >>> from zope.component import getUtility >>> message_store = getUtility(IMessageStore) - >>> preserved_message = getUtility(IMessageStore).get_message_by_id( - ... '<dolphin>') - >>> print preserved_message['message-id'] + >>> print message_store.get_message_by_id('<dolphin>')['message-id'] <dolphin> Orthogonal to preservation, the message can also be forwarded to another @@ -213,8 +211,9 @@ moderators. ... ... Here's something important about our mailing list. ... """) - >>> id = hold_message(mlist, msg, {}, 'Needs approval') - >>> handle_message(mlist, id, Action.discard, forward=['zack@example.com']) + >>> hold_message(mlist, msg, {}, 'Needs approval') + 2 + >>> handle_message(mlist, 2, Action.discard, forward=['zack@example.com']) The forwarded message is in the virgin queue, destined for the moderator. :: @@ -244,47 +243,11 @@ choosing and their preferred language. >>> from mailman.app.moderator import hold_subscription >>> from mailman.interfaces.member import DeliveryMode - >>> mlist.admin_immed_notify = False >>> hold_subscription(mlist, ... 'fred@example.org', 'Fred Person', ... '{NONE}abcxyz', DeliveryMode.regular, 'en') 2 -In the above case the mailing list was not configured to send the list -moderators a notice about the hold, so no email message is in the virgin -queue. - - >>> get_queue_messages('virgin') - [] - -But if we set the list up to notify the list moderators immediately when a -message is held for approval, there will be a message placed in the virgin -queue when the message is held. -:: - - >>> mlist.admin_immed_notify = True - >>> hold_subscription(mlist, - ... 'gwen@example.org', 'Gwen Person', - ... '{NONE}zyxcba', DeliveryMode.regular, 'en') - 3 - - >>> messages = get_queue_messages('virgin') - >>> len(messages) - 1 - - >>> print messages[0].msg.as_string() - MIME-Version: 1.0 - ... - Subject: New subscription request to A Test List from gwen@example.org - ... - <BLANKLINE> - Your authorization is required for a mailing list subscription request - approval: - <BLANKLINE> - For: gwen@example.org - List: ant@example.com - ... - Disposing of membership change requests --------------------------------------- @@ -304,16 +267,22 @@ The held subscription can also be discarded. >>> print requests.get_request(2) None -The request can be rejected, in which case a message is sent to the -subscriber. -:: +Gwen tries to subscribe to the mailing list, but... + + >>> hold_subscription(mlist, + ... 'gwen@example.org', 'Gwen Person', + ... '{NONE}zyxcba', DeliveryMode.regular, 'en') + 2 - >>> handle_subscription(mlist, 3, Action.reject, - ... 'This is a closed list') +...her request is rejected... + + >>> handle_subscription(mlist, 2, Action.reject, 'This is a closed list') >>> messages = get_queue_messages('virgin') >>> len(messages) 1 +...and she receives a rejection notice. + >>> print messages[0].msg.as_string() MIME-Version: 1.0 ... @@ -340,29 +309,6 @@ mailing list. ... 'abcxyz', DeliveryMode.regular, 'en') 2 -A message will be sent to the moderators telling them about the held -subscription and the fact that they may need to approve it. -:: - - >>> messages = get_queue_messages('virgin') - >>> len(messages) - 1 - - >>> print messages[0].msg.as_string() - MIME-Version: 1.0 - ... - Subject: New subscription request to A Test List from herb@example.org - From: ant-owner@example.com - To: ant-owner@example.com - ... - <BLANKLINE> - Your authorization is required for a mailing list subscription request - approval: - <BLANKLINE> - For: herb@example.org - List: ant@example.com - ... - The moderators accept the subscription request. >>> handle_subscription(mlist, 2, Action.accept) @@ -373,10 +319,6 @@ And now Herb is a member of the mailing list. Herb Person <herb@example.org> -.. Clear the queue. - >>> ignore = get_queue_messages('virgin') - - Holding unsubscription requests =============================== @@ -385,7 +327,6 @@ the unsubscribing address is required. Herb now wants to leave the mailing list, but his request must be approved. - >>> mlist.admin_immed_notify = False >>> from mailman.app.moderator import hold_unsubscription >>> hold_unsubscription(mlist, 'herb@example.org') 2 @@ -448,11 +389,150 @@ the mailing list. None -Membership change notifications -=============================== +Notifications +============= + +Membership change requests +-------------------------- + +Usually, the list administrators want to be notified when there are membership +change requests they need to moderate. These notifications are sent when the +list is configured to send them. + + >>> mlist.admin_immed_notify = True + +Iris tries to subscribe to the mailing list. -TBD: + >>> hold_subscription(mlist, 'iris@example.org', 'Iris Person', + ... 'password', DeliveryMode.regular, 'en') + 2 - * admin_immed_notify - * welcome messages - * goodbye messages +There's now a message in the virgin queue, destined for the list owner. + + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print messages[0].msg.as_string() + MIME-Version: 1.0 + ... + Subject: New subscription request to A Test List from iris@example.org + From: ant-owner@example.com + To: ant-owner@example.com + ... + Your authorization is required for a mailing list subscription request + approval: + <BLANKLINE> + For: iris@example.org + List: ant@example.com + ... + +Similarly, the administrator gets notifications on unsubscription requests. +Jeff is a member of the mailing list, and chooses to unsubscribe. + + >>> hold_unsubscription(mlist, 'jeff@example.org') + 3 + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print messages[0].msg.as_string() + MIME-Version: 1.0 + ... + Subject: New unsubscription request from A Test List by jeff@example.org + From: ant-owner@example.com + To: ant-owner@example.com + ... + Your authorization is required for a mailing list unsubscription + request approval: + <BLANKLINE> + By: jeff@example.org + From: ant@example.com + ... + + +Membership changes +------------------ + +When a new member request is accepted, the mailing list administrators can +receive a membership change notice. + + >>> mlist.admin_notify_mchanges = True + >>> mlist.admin_immed_notify = False + >>> handle_subscription(mlist, 2, Action.accept) + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print messages[0].msg.as_string() + MIME-Version: 1.0 + ... + Subject: A Test List subscription notification + From: noreply@example.com + To: ant-owner@example.com + ... + Iris Person <iris@example.org> has been successfully subscribed to A + Test List. + +Similarly when an unsubscription request is accepted, the administrators can +get a notification. + + >>> hold_unsubscription(mlist, 'iris@example.org') + 4 + >>> handle_unsubscription(mlist, 4, Action.accept) + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print messages[0].msg.as_string() + MIME-Version: 1.0 + ... + Subject: A Test List unsubscription notification + From: noreply@example.com + To: ant-owner@example.com + ... + Iris Person <iris@example.org> has been removed from A Test List. + + +Welcome messages +---------------- + +When a member is subscribed to the mailing list via moderator approval, she +can get a welcome message. + + >>> mlist.admin_notify_mchanges = False + >>> mlist.send_welcome_message = True + >>> hold_subscription(mlist, 'kate@example.org', 'Kate Person', + ... 'password', DeliveryMode.regular, 'en') + 4 + >>> handle_subscription(mlist, 4, Action.accept) + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print messages[0].msg.as_string() + MIME-Version: 1.0 + ... + Subject: Welcome to the "A Test List" mailing list + From: ant-request@example.com + To: Kate Person <kate@example.org> + ... + Welcome to the "A Test List" mailing list! + ... + + +Goodbye messages +---------------- + +Similarly, when the member's unsubscription request is approved, she'll get a +goodbye message. + + >>> mlist.send_goodbye_message = True + >>> hold_unsubscription(mlist, 'kate@example.org') + 4 + >>> handle_unsubscription(mlist, 4, Action.accept) + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 1 + >>> print messages[0].msg.as_string() + MIME-Version: 1.0 + ... + Subject: You have been unsubscribed from the A Test List mailing list + From: ant-bounces@example.com + To: kate@example.org + ... diff --git a/src/mailman/app/notifications.py b/src/mailman/app/notifications.py index bc5b326ac..841822c84 100644 --- a/src/mailman/app/notifications.py +++ b/src/mailman/app/notifications.py @@ -47,6 +47,24 @@ log = logging.getLogger('mailman.error') +def _get_message(uri_template, mlist, language): + if not uri_template: + return '' + try: + uri = expand(uri_template, dict( + listname=mlist.fqdn_listname, + language=language.code, + )) + message = getUtility(ITemplateLoader).get(uri) + except URLError: + log.exception('Message URI not found ({0}): {1}'.format( + mlist.fqdn_listname, uri_template)) + return '' + else: + return wrap(message) + + + def send_welcome_message(mlist, address, language, delivery_mode, text=''): """Send a welcome message to a subscriber. @@ -62,28 +80,15 @@ def send_welcome_message(mlist, address, language, delivery_mode, text=''): :param delivery_mode: the type of delivery the subscriber is getting :type delivery_mode: DeliveryMode """ - if mlist.welcome_message_uri: - try: - uri = expand(mlist.welcome_message_uri, dict( - listname=mlist.fqdn_listname, - language=language.code, - )) - welcome_message = getUtility(ITemplateLoader).get(uri) - except URLError: - log.exception('Welcome message URI not found ({0}): {1}'.format( - mlist.fqdn_listname, mlist.welcome_message_uri)) - welcome = '' - else: - welcome = wrap(welcome_message) - else: - welcome = '' + welcome_message = _get_message(mlist.welcome_message_uri, + mlist, language) # Find the IMember object which is subscribed to the mailing list, because # from there, we can get the member's options url. member = mlist.members.get_member(address) user_name = member.user.display_name options_url = member.options_url # Get the text from the template. - text = expand(welcome, dict( + text = expand(welcome_message, dict( fqdn_listname=mlist.fqdn_listname, list_name=mlist.display_name, listinfo_uri=mlist.script_url('listinfo'), @@ -119,15 +124,13 @@ def send_goodbye_message(mlist, address, language): :param language: the language of the response :type language: string """ - if mlist.goodbye_msg: - goodbye = wrap(mlist.goodbye_msg) + '\n' - else: - goodbye = '' + goodbye_message = _get_message(mlist.goodbye_message_uri, + mlist, language) msg = UserNotification( address, mlist.bounces_address, _('You have been unsubscribed from the $mlist.display_name ' 'mailing list'), - goodbye, language) + goodbye_message, language) msg.send(mlist, verp=as_boolean(config.mta.verp_personalized_deliveries)) diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst index 0fa00ab86..9e664d470 100644 --- a/src/mailman/docs/NEWS.rst +++ b/src/mailman/docs/NEWS.rst @@ -16,10 +16,12 @@ Compatibility ------------- * Python 2.7 is not required. Python 2.6 is no longer officially supported. The code base is now also `python2.7 -3` clean, although there are still - some warnings in 3rd party dependencies. LP: #1073506 + some warnings in 3rd party dependencies. (LP: #1073506) REST ---- + * Expose a REST API for membership change (subscriptions and unsubscriptions) + moderation. (LP: #1090753) * Add list_id to JSON representation for a mailing list (given by Jimmy Bergman). * The canonical resource for a mailing list (and thus its self_link) is now @@ -69,6 +71,10 @@ Commands * `bin/mailman start` was passing the wrong relative path to its runner subprocesses when -C was given. LP: #982551 +Bugs +---- + * Fixed `send_goodbye_message()`. (LP: #1091321) + 3.0 beta 2 -- "Freeze" ====================== |
