summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/app/subscriptions.py19
-rw-r--r--src/mailman/interfaces/mailinglist.py14
-rw-r--r--src/mailman/interfaces/subscriptions.py63
-rw-r--r--src/mailman/model/mailinglist.py29
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,