diff options
| author | Barry Warsaw | 2015-04-13 12:16:28 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2015-04-13 12:16:28 -0400 |
| commit | c595c3d907f111d80852837c686b23aea9ee40c1 (patch) | |
| tree | c28571aebb3642a1b6f33572855374d30c8085e4 | |
| parent | 9806f9c751f85b6ad6e9dea58d9e3dde36c7f2bc (diff) | |
| download | mailman-c595c3d907f111d80852837c686b23aea9ee40c1.tar.gz mailman-c595c3d907f111d80852837c686b23aea9ee40c1.tar.zst mailman-c595c3d907f111d80852837c686b23aea9ee40c1.zip | |
| -rw-r--r-- | TODO.rst | 10 | ||||
| -rw-r--r-- | src/mailman/app/subscriptions.py | 56 | ||||
| -rw-r--r-- | src/mailman/app/tests/test_subscriptions.py | 60 | ||||
| -rw-r--r-- | src/mailman/templates/en/subauth.txt | 6 |
4 files changed, 111 insertions, 21 deletions
@@ -1,8 +1,4 @@ * TO DO: - - add full RequestRecord to SubscriptionWorkflow ctor - - hook up sending of confirmation - - processing confirmations and continuing workflow - - get tokens for saving workflows - - integrate with RequestRecord - - integrate with hold_subscription - - after getting moderator approval, continue workflow + - get rid of hold_subscription + - subsume handle_subscription + - workflow for unsubscription diff --git a/src/mailman/app/subscriptions.py b/src/mailman/app/subscriptions.py index 22f4bdf56..982c04547 100644 --- a/src/mailman/app/subscriptions.py +++ b/src/mailman/app/subscriptions.py @@ -26,24 +26,30 @@ __all__ = [ import uuid +import logging +from email.utils import formataddr from enum import Enum +from datetime import timedelta from mailman.app.membership import add_member, delete_member -from mailman.app.moderator import hold_subscription from mailman.app.workflow import Workflow from mailman.core.constants import system_preferences +from mailman.core.i18n import _ from mailman.database.transaction import dbconnection +from mailman.email.message import UserNotification from mailman.interfaces.address import IAddress from mailman.interfaces.listmanager import ( IListManager, ListDeletingEvent, NoSuchListError) from mailman.interfaces.mailinglist import SubscriptionPolicy from mailman.interfaces.member import DeliveryMode, MemberRole +from mailman.interfaces.pending import IPendable, IPendings from mailman.interfaces.subscriptions import ( ISubscriptionService, MissingUserError, RequestRecord) from mailman.interfaces.user import IUser from mailman.interfaces.usermanager import IUserManager from mailman.model.member import Member from mailman.utilities.datetime import now +from mailman.utilities.i18n import make from operator import attrgetter from sqlalchemy import and_, or_ from uuid import UUID @@ -51,6 +57,9 @@ from zope.component import getUtility from zope.interface import implementer +log = logging.getLogger('mailman.subscribe') + + def _membership_sort_key(member): """Sort function for find_members(). @@ -66,6 +75,11 @@ class WhichSubscriber(Enum): user = 2 +@implementer(IPendable) +class Pendable(dict): + pass + + class SubscriptionWorkflow(Workflow): """Workflow of a subscription request.""" @@ -201,18 +215,44 @@ class SubscriptionWorkflow(Workflow): self.mlist.subscribe(self.subscriber) def _step_get_moderator_approval(self): - # In order to get the moderator's approval, we need to hold the - # subscription request in the database - request = RequestRecord( - self.address.email, self.subscriber.display_name, - # XXX Need to get these last to into the constructor. - DeliveryMode.regular, 'en') - self.token = hold_subscription(self.mlist, request) + # Getting the moderator's approval requires several steps. We'll need + # to suspend this workflow for an indeterminate amount of time while + # we wait for that approval. We need a unique token for this + # suspended workflow, so we'll create a minimal pending record. We + # also might need to send an email notification to the list + # moderators. + # + # Start by creating the pending record. This will give us a hash + # token we can use to uniquely name this workflow. It only needs to + # contain the current date, which we'll use to expire requests from + # the database, say if the moderator never approves the request. + pendable = Pendable(when=now().isoformat()) + self.token = getUtility(IPendings).add(pendable, timedelta(days=3650)) # Here's the next step in the workflow, assuming the moderator # approves of the subscription. If they don't, the workflow and # subscription request will just be thrown away. self.push('subscribe_from_restored') self.save() + log.info('{}: held subscription request from {}'.format( + self.mlist.fqdn_listname, self.address.email)) + # Possibly send a notification to the list moderators. + if self.mlist.admin_immed_notify: + subject = _( + 'New subscription request to $self.mlist.display_name ' + 'from $self.address.email') + username = formataddr( + (self.subscriber.display_name, self.address.email)) + text = make('subauth.txt', + mailing_list=self.mlist, + username=username, + listname=self.mlist.fqdn_listname, + ) + # This message should appear to come from the <list>-owner so as + # to avoid any useless bounce processing. + msg = UserNotification( + self.mlist.owner_address, self.mlist.owner_address, + subject, text, self.mlist.preferred_language) + msg.send(self.mlist, tomoderators=True) # The workflow must stop running here. raise StopIteration diff --git a/src/mailman/app/tests/test_subscriptions.py b/src/mailman/app/tests/test_subscriptions.py index 61d341d6d..45e17a9e5 100644 --- a/src/mailman/app/tests/test_subscriptions.py +++ b/src/mailman/app/tests/test_subscriptions.py @@ -33,6 +33,7 @@ from mailman.interfaces.member import MemberRole, MissingPreferredAddressError from mailman.interfaces.requests import IListRequests, RequestType from mailman.interfaces.subscriptions import ( MissingUserError, ISubscriptionService) +from mailman.testing.helpers import LogFileMark, get_queue_messages from mailman.testing.layers import ConfigLayer from mailman.interfaces.mailinglist import SubscriptionPolicy from mailman.interfaces.usermanager import IUserManager @@ -77,9 +78,11 @@ class TestJoin(unittest.TestCase): class TestSubscriptionWorkflow(unittest.TestCase): layer = ConfigLayer + maxDiff = None def setUp(self): self._mlist = create_list('test@example.com') + self._mlist.admin_immed_notify = False self._anne = 'anne@example.com' self._user_manager = getUtility(IUserManager) @@ -350,6 +353,63 @@ class TestSubscriptionWorkflow(unittest.TestCase): member = self._mlist.regular_members.get_member(self._anne) self.assertEqual(member.address, anne) + def test_get_moderator_approval_log_on_hold(self): + # When the subscription is held for moderator approval, a message is + # logged. + mark = LogFileMark('mailman.subscribe') + self._mlist.subscription_policy = SubscriptionPolicy.moderate + anne = self._user_manager.create_address(self._anne) + workflow = SubscriptionWorkflow(self._mlist, anne, + pre_verified=True, + pre_confirmed=True) + # Consume the entire state machine. + list(workflow) + line = mark.readline() + self.assertEqual( + line[29:-1], + 'test@example.com: held subscription request from anne@example.com' + ) + + def test_get_moderator_approval_notifies_moderators(self): + # When the subscription is held for moderator approval, and the list + # is so configured, a notification is sent to the list moderators. + self._mlist.admin_immed_notify = True + self._mlist.subscription_policy = SubscriptionPolicy.moderate + anne = self._user_manager.create_address(self._anne) + workflow = SubscriptionWorkflow(self._mlist, anne, + pre_verified=True, + pre_confirmed=True) + # Consume the entire state machine. + list(workflow) + items = get_queue_messages('virgin') + self.assertEqual(len(items), 1) + message = items[0].msg + self.assertEqual(message['From'], 'test-owner@example.com') + self.assertEqual(message['To'], 'test-owner@example.com') + self.assertEqual( + message['Subject'], + 'New subscription request to Test from anne@example.com') + self.assertEqual(message.get_payload(), """\ +Your authorization is required for a mailing list subscription request +approval: + + For: anne@example.com + List: test@example.com""") + + def test_get_moderator_approval_no_notifications(self): + # When the subscription is held for moderator approval, and the list + # is so configured, a notification is sent to the list moderators. + self._mlist.admin_immed_notify = False + self._mlist.subscription_policy = SubscriptionPolicy.moderate + anne = self._user_manager.create_address(self._anne) + workflow = SubscriptionWorkflow(self._mlist, anne, + pre_verified=True, + pre_confirmed=True) + # Consume the entire state machine. + list(workflow) + items = get_queue_messages('virgin') + self.assertEqual(len(items), 0) + # XXX @unittest.expectedFailure diff --git a/src/mailman/templates/en/subauth.txt b/src/mailman/templates/en/subauth.txt index 1b13ebaeb..041be5e55 100644 --- a/src/mailman/templates/en/subauth.txt +++ b/src/mailman/templates/en/subauth.txt @@ -3,9 +3,3 @@ approval: For: $username List: $listname - -At your convenience, visit: - - $admindb_url - -to process the request. |
