summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/docs/NEWS.rst15
-rw-r--r--src/mailman/interfaces/member.py31
-rw-r--r--src/mailman/model/docs/membership.rst23
-rw-r--r--src/mailman/model/mailinglist.py5
-rw-r--r--src/mailman/model/member.py6
5 files changed, 72 insertions, 8 deletions
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst
index 64df34ef1..b9720b960 100644
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -73,18 +73,21 @@ Interfaces
`IMailingList` to an `IBanManager` to manage the bans for a specific
mailing list. To manage the global bans, adapt ``None``.
-Integration
------------
- * Added support for Postfix `relay_domains` setting for better virtual domain
- support. Contributed by Jimmy Bergman.
-
Commands
--------
* `bin/mailman aliases` loses the `--output`, `--format`, and `--simple`
arguments, and adds a `--directory` argument. This is necessary to support
the Postfix `relay_domains` support.
* `bin/mailman start` was passing the wrong relative path to its runner
- subprocesses when -C was given. LP: #982551
+ subprocesses when -C was given. (LP: #982551)
+
+Other
+-----
+ * Added support for Postfix `relay_domains` setting for better virtual domain
+ support. Contributed by Jimmy Bergman.
+ * Two new events are triggered on membership changes: `SubscriptionEvent`
+ when a new member joins a mailing list, and an `UnsubscriptionEvent` when a
+ member leaves a mailing list. (LP: #1047286)
Bugs
----
diff --git a/src/mailman/interfaces/member.py b/src/mailman/interfaces/member.py
index 997338835..815bda2d4 100644
--- a/src/mailman/interfaces/member.py
+++ b/src/mailman/interfaces/member.py
@@ -26,10 +26,13 @@ __all__ = [
'DeliveryStatus',
'IMember',
'MemberRole',
+ 'MembershipChangeEvent',
'MembershipError',
'MembershipIsBannedError',
'MissingPreferredAddressError',
'NotAMemberError',
+ 'SubscriptionEvent',
+ 'UnsubscriptionEvent',
]
@@ -74,6 +77,34 @@ class MemberRole(Enum):
+class MembershipChangeEvent:
+ """Base class for subscription/unsubscription events."""
+
+ def __init__(self, mlist, member):
+ self.mlist = mlist
+ self.member = member
+
+
+class SubscriptionEvent(MembershipChangeEvent):
+ """Event which gets triggered when a user joins a mailing list."""
+
+ def __str__(self):
+ return '{0} joined {1}'.format(self.member.address, self.mlist.list_id)
+
+
+class UnsubscriptionEvent(MembershipChangeEvent):
+ """Event which gets triggered when a user leaves a mailing list.
+
+ One thing to keep in mind: because the IMember is deleted when the
+ unsubscription happens, this event actually gets triggered just before the
+ member is unsubscribed.
+ """
+
+ def __str__(self):
+ return '{0} left {1}'.format(self.member.address, self.mlist.list_id)
+
+
+
class MembershipError(MailmanError):
"""Base exception for all membership errors."""
diff --git a/src/mailman/model/docs/membership.rst b/src/mailman/model/docs/membership.rst
index 3286bfe6e..f257f25ce 100644
--- a/src/mailman/model/docs/membership.rst
+++ b/src/mailman/model/docs/membership.rst
@@ -315,3 +315,26 @@ Now her membership reflects the new address.
>>> for m in bee.members.members:
... print m.member_id.int, m.mailing_list.list_id, m.address.email
7 bee.example.com gperson@example.com
+
+
+Events
+======
+
+An event is triggered when a new member is subscribed to a mailing list.
+::
+
+ >>> from mailman.testing.helpers import event_subscribers
+ >>> def handle_event(event):
+ ... print event
+
+ >>> cat = create_list('cat@example.com')
+ >>> herb = user_manager.create_address('herb@example.com')
+ >>> with event_subscribers(handle_event):
+ ... member = cat.subscribe(herb)
+ herb@example.com joined cat.example.com
+
+An event is triggered when a member is unsubscribed from a mailing list.
+
+ >>> with event_subscribers(handle_event):
+ ... member.unsubscribe()
+ herb@example.com left cat.example.com
diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py
index 2c55540be..324e1a1c8 100644
--- a/src/mailman/model/mailinglist.py
+++ b/src/mailman/model/mailinglist.py
@@ -33,6 +33,7 @@ from storm.locals import (
TimeDelta, Unicode)
from urlparse import urljoin
from zope.component import getUtility
+from zope.event import notify
from zope.interface import implementer
from mailman.config import config
@@ -50,7 +51,8 @@ from mailman.interfaces.mailinglist import (
IAcceptableAlias, IAcceptableAliasSet, IMailingList, Personalization,
ReplyToMunging)
from mailman.interfaces.member import (
- AlreadySubscribedError, MemberRole, MissingPreferredAddressError)
+ AlreadySubscribedError, MemberRole, MissingPreferredAddressError,
+ SubscriptionEvent)
from mailman.interfaces.mime import FilterType
from mailman.interfaces.nntp import NewsgroupModeration
from mailman.interfaces.user import IUser
@@ -493,6 +495,7 @@ class MailingList(Model):
subscriber=subscriber)
member.preferences = Preferences()
store.add(member)
+ notify(SubscriptionEvent(self, member))
return member
diff --git a/src/mailman/model/member.py b/src/mailman/model/member.py
index 76fe2f992..c189983b9 100644
--- a/src/mailman/model/member.py
+++ b/src/mailman/model/member.py
@@ -27,6 +27,7 @@ __all__ = [
from storm.locals import Int, Reference, Unicode
from storm.properties import UUID
from zope.component import getUtility
+from zope.event import notify
from zope.interface import implementer
from mailman.core.constants import system_preferences
@@ -36,7 +37,8 @@ from mailman.database.types import Enum
from mailman.interfaces.action import Action
from mailman.interfaces.address import IAddress
from mailman.interfaces.listmanager import IListManager
-from mailman.interfaces.member import IMember, MemberRole, MembershipError
+from mailman.interfaces.member import (
+ IMember, MemberRole, MembershipError, UnsubscriptionEvent)
from mailman.interfaces.user import IUser, UnverifiedAddressError
from mailman.interfaces.usermanager import IUserManager
from mailman.utilities.uid import UniqueIDFactory
@@ -186,5 +188,7 @@ class Member(Model):
@dbconnection
def unsubscribe(self, store):
"""See `IMember`."""
+ # Yes, this must get triggered before self is deleted.
+ notify(UnsubscriptionEvent(self.mailing_list, self))
store.remove(self.preferences)
store.remove(self)