summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mailman/database/roster.py58
-rw-r--r--mailman/database/user.py5
-rw-r--r--mailman/docs/users.txt48
-rw-r--r--mailman/interfaces/user.py3
4 files changed, 112 insertions, 2 deletions
diff --git a/mailman/database/roster.py b/mailman/database/roster.py
index 2b237df97..41271dc36 100644
--- a/mailman/database/roster.py
+++ b/mailman/database/roster.py
@@ -22,6 +22,19 @@ the ones that fit a particular role. These are used as the member, owner,
moderator, and administrator roster filters.
"""
+__metaclass__ = type
+__all__ = [
+ 'AdministratorRoster',
+ 'DigestMemberRoster',
+ 'MemberRoster',
+ 'Memberships',
+ 'ModeratorRoster',
+ 'OwnerRoster',
+ 'RegularMemberRoster',
+ 'Subscribers',
+ ]
+
+
from storm.locals import *
from zope.interface import implements
@@ -33,7 +46,7 @@ from mailman.interfaces import DeliveryMode, IRoster, MemberRole
-class AbstractRoster(object):
+class AbstractRoster:
"""An abstract IRoster class.
This class takes the simple approach of implemented the 'users' and
@@ -208,3 +221,46 @@ class Subscribers(AbstractRoster):
Member,
mailing_list=self._mlist.fqdn_listname):
yield member
+
+
+
+class Memberships:
+ """A roster of a single user's memberships."""
+
+ implements(IRoster)
+
+ name = 'memberships'
+
+ def __init__(self, user):
+ self._user = user
+
+ @property
+ def members(self):
+ results = config.db.store.find(
+ Member,
+ Address.user_id == self._user.id,
+ Member.address_id == Address.id)
+ for member in results:
+ yield member
+
+ @property
+ def users(self):
+ yield self._user
+
+ @property
+ def addresses(self):
+ for address in self._user.addresses:
+ yield address
+
+ def get_member(self, address):
+ results = config.db.store.find(
+ Member,
+ Member.address_id == Address.id,
+ Address.user_id == self._user.id)
+ if results.count() == 0:
+ return None
+ elif results.count() == 1:
+ return results[0]
+ else:
+ raise AssertionError('Too many matching member results: %s' %
+ results.count())
diff --git a/mailman/database/user.py b/mailman/database/user.py
index 308eeb10d..9f8d8b812 100644
--- a/mailman/database/user.py
+++ b/mailman/database/user.py
@@ -23,6 +23,7 @@ from mailman.configuration import config
from mailman.database.model import Model
from mailman.database.address import Address
from mailman.database.preferences import Preferences
+from mailman.database.roster import Memberships
from mailman.interfaces import (
AddressAlreadyLinkedError, AddressNotLinkedError, IUser)
@@ -78,3 +79,7 @@ class User(Model):
raise AddressAlreadyLinkedError(addrobj)
addrobj.user = self
return addrobj
+
+ @property
+ def memberships(self):
+ return Memberships(self)
diff --git a/mailman/docs/users.txt b/mailman/docs/users.txt
index 032f0720b..e9ee4ca50 100644
--- a/mailman/docs/users.txt
+++ b/mailman/docs/users.txt
@@ -3,7 +3,7 @@ Users
Users are entities that represent people. A user has a real name and a
password. Optionally a user may have some preferences and a set of addresses
-they control.
+they control. A user also knows which mailing lists they are subscribed to.
See usermanager.txt for examples of how to create, delete, and find users.
@@ -146,3 +146,49 @@ Some of these preferences are booleans and they can be set to True or False.
receive_list_copy : False
receive_own_postings : False
delivery_mode : DeliveryMode.regular
+
+
+Subscriptions
+-------------
+
+Users know which mailing lists they are subscribed to, regardless of
+membership role.
+
+ >>> user_1.link(address_1)
+ >>> sorted(address.address for address in user_1.addresses)
+ [u'zperson@example.com', u'zperson@example.net', u'zperson@example.org']
+ >>> com = usermgr.get_address(u'zperson@example.com')
+ >>> org = usermgr.get_address(u'zperson@example.org')
+ >>> net = usermgr.get_address(u'zperson@example.net')
+
+ >>> from mailman.app.lifecycle import create_list
+ >>> mlist_1 = create_list(u'xtest_1@example.com')
+ >>> mlist_2 = create_list(u'xtest_2@example.com')
+ >>> mlist_3 = create_list(u'xtest_3@example.com')
+ >>> from mailman.interfaces import MemberRole
+
+ >>> com.subscribe(mlist_1, MemberRole.member)
+ <Member: Zoe Person <zperson@example.com> on xtest_1@example.com as
+ MemberRole.member>
+ >>> org.subscribe(mlist_2, MemberRole.member)
+ <Member: zperson@example.org on xtest_2@example.com as MemberRole.member>
+ >>> org.subscribe(mlist_2, MemberRole.owner)
+ <Member: zperson@example.org on xtest_2@example.com as MemberRole.owner>
+ >>> net.subscribe(mlist_3, MemberRole.moderator)
+ <Member: zperson@example.net on xtest_3@example.com as
+ MemberRole.moderator>
+
+ >>> memberships = user_1.memberships
+ >>> from mailman.interfaces import IRoster
+ >>> from zope.interface.verify import verifyObject
+ >>> verifyObject(IRoster, memberships)
+ True
+ >>> members = sorted(memberships.members)
+ >>> len(members)
+ 4
+ >>> for member in sorted(members, key=lambda m: m.address.address):
+ ... print member.address.address, member.mailing_list, member.role
+ zperson@example.com xtest_1@example.com MemberRole.member
+ zperson@example.net xtest_3@example.com MemberRole.moderator
+ zperson@example.org xtest_2@example.com MemberRole.member
+ zperson@example.org xtest_2@example.com MemberRole.owner
diff --git a/mailman/interfaces/user.py b/mailman/interfaces/user.py
index 25703bd17..0e84f1ab0 100644
--- a/mailman/interfaces/user.py
+++ b/mailman/interfaces/user.py
@@ -33,6 +33,9 @@ class IUser(Interface):
addresses = Attribute(
"""An iterator over all the IAddresses controlled by this user.""")
+ memberships = Attribute(
+ """A roster of this user's membership.""")
+
def register(address, real_name=None):
"""Register the given email address and link it to this user.