diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/app/subscriptions.py | 19 | ||||
| -rw-r--r-- | src/mailman/interfaces/mailinglist.py | 14 | ||||
| -rw-r--r-- | src/mailman/interfaces/subscriptions.py | 63 | ||||
| -rw-r--r-- | src/mailman/model/mailinglist.py | 29 |
4 files changed, 115 insertions, 10 deletions
diff --git a/src/mailman/app/subscriptions.py b/src/mailman/app/subscriptions.py index fb712d979..52c0ddfbc 100644 --- a/src/mailman/app/subscriptions.py +++ b/src/mailman/app/subscriptions.py @@ -550,8 +550,19 @@ class BaseSubscriptionManager: def __init__(self, mlist): self._mlist = mlist + def _get_workflow(self): + raise NotImplementedError + + def register(self, subscriber=None, *, + pre_verified=False, pre_confirmed=False, pre_approved=False): + raise NotImplementedError + + def unregister(self, subscriber=None, *, + pre_confirmed=False, pre_approved=False): + raise NotImplementedError + def confirm(self, token): - workflow = self.__class__(self._mlist) + workflow = self._get_workflow() workflow.token = token workflow.restore() # In order to just run the whole workflow, all we need to do @@ -573,6 +584,9 @@ class BaseSubscriptionManager: class SubscriptionWorkflowManager(BaseSubscriptionManager): """Handle registrations and confirmations for subscriptions.""" + def _get_workflow(self): + return SubscriptionWorkflow(self._mlist) + def register(self, subscriber=None, *, pre_verified=False, pre_confirmed=False, pre_approved=False): """See `ISubscriptionManager`.""" @@ -590,6 +604,9 @@ class SubscriptionWorkflowManager(BaseSubscriptionManager): class UnsubscriptionWorkflowManager(BaseSubscriptionManager): """Handle un-subscriptions and confirmations for un-subscriptions.""" + def _get_workflow(self): + return UnSubscriptionWorkflow(self._mlist) + def unregister(self, subscriber=None, *, pre_confirmed=False, pre_approved=False): workflow = UnSubscriptionWorkflow( diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py index 366016872..2a862cde2 100644 --- a/src/mailman/interfaces/mailinglist.py +++ b/src/mailman/interfaces/mailinglist.py @@ -271,6 +271,18 @@ class IMailingList(Interface): :rtype: Roster """ + def is_subscribed(subscriber, role=MemberRole.member): + """Is the given address or user subscribed to the mailing list? + + :param subscriber: The address or user to check. + :type subscriber: `IUser` or `IAddress` + :param role: The role being checked (e.g. a member, owner, or + moderator of a mailing list). + :type role: `MemberRole` + :return: A flag indicating whether the subscriber is already + subscribed to the mailing list or not. + """ + def subscribe(subscriber, role=MemberRole.member): """Subscribe the given address or user to the mailing list. @@ -279,7 +291,7 @@ class IMailingList(Interface): has one, otherwise no address for the user appears in the rosters. :type subscriber: `IUser` or `IAddress` :param role: The role being subscribed to (e.g. a member, owner, or - moderator of a mailing list. + moderator of a mailing list). :type role: `MemberRole` :return: The member object representing the subscription. :rtype: `IMember` diff --git a/src/mailman/interfaces/subscriptions.py b/src/mailman/interfaces/subscriptions.py index 62f5be627..9a20b9c0d 100644 --- a/src/mailman/interfaces/subscriptions.py +++ b/src/mailman/interfaces/subscriptions.py @@ -224,6 +224,28 @@ class ISubscriptionManager(Interface): :param subscriber: The user or address to subscribe. :type email: ``IUser`` or ``IAddress`` + :param pre_verified: A flag indicating whether the subscriber's email + address should be considered pre-verified. Normally a never + before seen email address must be verified by mail-back + confirmation. Setting this flag to True automatically verifies + such addresses without the mail-back. (A confirmation message may + still be sent under other conditions.) + :type pre_verified: bool + :param pre_confirmed: A flag indicating whether, when required by the + subscription policy, a subscription request should be considered + pre-confirmed. Normally in such cases, a mail-back confirmation + message is sent to the subscriber, which must be positively + acknowledged by some manner. Setting this flag to True + automatically confirms the subscription request. (A confirmation + message may still be sent under other conditions.) + :type pre_confirmed: bool + :param pre_approved: A flag indicating whether, when required by the + subscription policy, a subscription request should be considered + pre-approved. Normally in such cases, the list administrator is + notified that an approval is necessary, which must be positively + acknowledged in some manner. Setting this flag to True + automatically approves the subscription request. + :type pre_approved: bool :return: A 3-tuple is returned where the first element is the token hash, the second element is a ``TokenOwner`, and the third element is the subscribed member. If the subscriber got subscribed @@ -235,6 +257,47 @@ class ISubscriptionManager(Interface): appears in the global or list-centric bans. """ + def unregister(subscriber=None, *, + pre_confirmed=False, pre_approved=False): + """Unsubscribe an address or user according to subscription policies. + + The mailing list's unsubscription policy is used to unsubscribe + `subscriber` from the given mailing list. The subscriber can be + an ``IUser`` or an ``IAddress``, and must already be subscribed to the + mailing list. + + The workflow may pause (i.e. be serialized, saved, and + suspended) when some out-of-band confirmation step is required. + For example, if the user must confirm, or the moderator must + approve the unsubscription. Use the ``confirm(token)`` method to + resume the workflow. + + :param subscriber: The user or address to unsubscribe. + :type email: ``IUser`` or ``IAddress`` + :param pre_confirmed: A flag indicating whether, when required by the + unsubscription policy, an unsubscription request should be + considered pre-confirmed. Normally in such cases, a mail-back + confirmation message is sent to the subscriber, which must be + positively acknowledged by some manner. Setting this flag to True + automatically confirms the unsubscription request. (A confirmation + message may still be sent under other conditions.) + :type pre_confirmed: bool + :param pre_approved: A flag indicating whether, when required by the + unsubscription policy, an unsubscription request should be + considered pre-approved. Normally in such cases, the list + administrator is notified that an approval is necessary, which + must be positively acknowledged in some manner. Setting this flag + to True automatically approves the unsubscription request. + :type pre_approved: bool + :return: A 3-tuple is returned where the first element is the token + hash, the second element is a ``TokenOwner`, and the third element + is the unsubscribing member. If the subscriber got unsubscribed + immediately, the token will be None and the member will be + an ``IMember``. If the unsubscription got held, the token + will be a hash and the member will be None. + :rtype: (str-or-None, ``TokenOwner``, ``IMember``-or-None) + """ + def confirm(token): """Continue any paused workflow. diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py index 4e76e61c1..eec58ad04 100644 --- a/src/mailman/model/mailinglist.py +++ b/src/mailman/model/mailinglist.py @@ -441,32 +441,45 @@ class MailingList(Model): else: raise ValueError('Undefined MemberRole: {}'.format(role)) - @dbconnection - def is_subscribed(self, store, subscriber, role=MemberRole.member): - """Check if a user/address is subscribed to this list.""" + def _get_subscriber(self, store, subscriber, role): + """Get some information about a user/address. + + Returns a 2-tuple of (member, email) for the given subscriber. If the + subscriber is is not an ``IAddress`` or ``IUser``, then a 2-tuple of + (None, None) is returned. If the subscriber is not already + subscribed, then (None, email) is returned. If the subscriber is an + ``IUser`` and does not have a preferred address, (member, None) is + returned. + """ member = None + email = None if IAddress.providedBy(subscriber): member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._address == subscriber).first() + email = subscriber.email elif IUser.providedBy(subscriber): if subscriber.preferred_address is None: raise MissingPreferredAddressError(subscriber) + email = subscriber.preferred_address.email member = store.query(Member).filter( Member.role == role, Member.list_id == self._list_id, Member._user == subscriber).first() + return member, email + + @dbconnection + def is_subscribed(self, store, subscriber, role=MemberRole.member): + """See `IMailingList`.""" + member, email = self._get_subscriber(store, subscriber, role) return member is not None @dbconnection def subscribe(self, store, subscriber, role=MemberRole.member): """See `IMailingList`.""" - if IAddress.providedBy(subscriber): - email = subscriber.email - elif IUser.providedBy(subscriber): - email = subscriber.preferred_address.email - if self.is_subscribed(subscriber, role): + member, email = self._get_subscriber(store, subscriber, role) + if member is not None: raise AlreadySubscribedError(self.fqdn_listname, email, role) member = Member(role=role, list_id=self._list_id, |
