summaryrefslogtreecommitdiff
path: root/Mailman/database/model/roster.py
diff options
context:
space:
mode:
authorBarry Warsaw2007-06-09 15:20:32 -0400
committerBarry Warsaw2007-06-09 15:20:32 -0400
commit3231fd628f6eea30bd6e2be56eb419ed0008d954 (patch)
tree176f80a7b4f72c410e30ab9ba3e3fe2deb1bb1fe /Mailman/database/model/roster.py
parente5c04e2a93a58d799dd3940a7935853eb1f2e3e4 (diff)
downloadmailman-3231fd628f6eea30bd6e2be56eb419ed0008d954.tar.gz
mailman-3231fd628f6eea30bd6e2be56eb419ed0008d954.tar.zst
mailman-3231fd628f6eea30bd6e2be56eb419ed0008d954.zip
Implement the new, simplified membership model. Rosters and RosterSets as
they were previously known are now gone. Rosters, rather than being a database entity that collects users, is now just a filter on the member database. This way, we can use generic rosters to search for regular members, digest members, owners, or moderators. More advanced rosters can do all kinds of other membership queries. But rosters no longer need to be a database entity. Users have a name, password, optional preferences, and a set of addresses, but users are not subscribed to mailing lists. Addresses have the email address, some verification information, and optional preferences. Members tie an address to a mailing list, through a role, with optional preferences. Other changes here include: MailList.fqdn_listname() moved to the MailingList model entity. Added MemberRole enum and SystemDefaultPreferences to Mailman.constants. Profiles are renamed to Preferences (same with the interface), but the files are not yet moved. This happens later. We mostly don't need has_*() relationships on the entity classes, because we generally don't need the reverse relationship. Use belongs_to() because that creates the foreign key, even though the wording seems counter intuitive. IAddress.subscribe() added. Tell Elixir to use shortnames for all tables. Remove the OldStyleMembership fields from MailingList. Remove all the interface elements and database fields that talk about rosters and rostersets. Convert Version entity to has_field().
Diffstat (limited to 'Mailman/database/model/roster.py')
-rw-r--r--Mailman/database/model/roster.py177
1 files changed, 152 insertions, 25 deletions
diff --git a/Mailman/database/model/roster.py b/Mailman/database/model/roster.py
index bf8447433..03aa9efc3 100644
--- a/Mailman/database/model/roster.py
+++ b/Mailman/database/model/roster.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -15,37 +15,164 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.
-from elixir import *
+"""An implementation of an IRoster.
+
+These are hard-coded rosters which know how to filter a set of members to find
+the ones that fit a particular role. These are used as the member, owner,
+moderator, and administrator roster filters.
+"""
+
from zope.interface import implements
-from Mailman.Errors import ExistingAddressError
+from Mailman.constants import DeliveryMode, MemberRole
+from Mailman.database.model import Member
from Mailman.interfaces import IRoster
-ADDRESS_KIND = 'Mailman.database.model.address.Address'
-ROSTERSET_KIND = 'Mailman.database.model.rosterset.RosterSet'
+
+class AbstractRoster(object):
+ """An abstract IRoster class.
+ This class takes the simple approach of implemented the 'users' and
+ 'addresses' properties in terms of the 'members' property. This may not
+ be the most efficient way, but it works.
-class Roster(Entity):
+ This requires that subclasses implement the 'members' property.
+ """
implements(IRoster)
- has_field('name', Unicode)
- # Relationships
- has_and_belongs_to_many('addresses', of_kind=ADDRESS_KIND)
- has_and_belongs_to_many('roster_set', of_kind=ROSTERSET_KIND)
+ def __init__(self, mlist):
+ self._mlist = mlist
+
+ @property
+ def members(self):
+ raise NotImplementedError
+
+ @property
+ def users(self):
+ # Members are linked to addresses, which in turn are linked to users.
+ # So while the 'members' attribute does most of the work, we have to
+ # keep a set of unique users. It's possible for the same user to be
+ # subscribed to a mailing list multiple times with different
+ # addresses.
+ users = set(member.address.user for member in self.members)
+ for user in users:
+ yield user
+
+ @property
+ def addresses(self):
+ # Every Member is linked to exactly one address so the 'members'
+ # attribute does most of the work.
+ for member in self.members:
+ yield member.address
+
+
+
+class MemberRoster(AbstractRoster):
+ """Return all the members of a list."""
+
+ name = 'member'
+
+ @property
+ def members(self):
+ # Query for all the Members which have a role of MemberRole.member and
+ # are subscribed to this mailing list. XXX we have to use a private
+ # data attribute of MailList for now.
+ for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.member):
+ yield member
+
+
+
+class OwnerRoster(AbstractRoster):
+ """Return all the owners of a list."""
+
+ name = 'owner'
+
+ @property
+ def members(self):
+ # Query for all the Members which have a role of MemberRole.member and
+ # are subscribed to this mailing list. XXX we have to use a private
+ # data attribute of MailList for now.
+ for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.owner):
+ yield member
+
+
+
+class ModeratorRoster(AbstractRoster):
+ """Return all the owners of a list."""
+
+ name = 'moderator'
+
+ @property
+ def members(self):
+ # Query for all the Members which have a role of MemberRole.member and
+ # are subscribed to this mailing list. XXX we have to use a private
+ # data attribute of MailList for now.
+ for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.moderator):
+ yield member
+
+
+
+class AdministratorRoster(AbstractRoster):
+ """Return all the administrators of a list."""
+
+ name = 'administrator'
+
+ @property
+ def members(self):
+ # Administrators are defined as the union of the owners and the
+ # moderators. Until I figure out a more efficient way of doing this,
+ # this will have to do.
+ owners = Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.owner)
+ moderators = Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.moderator)
+ members = set(owners)
+ members.update(set(moderators))
+ for member in members:
+ yield member
+
+
+
+class RegularMemberRoster(AbstractRoster):
+ """Return all the regular delivery members of a list."""
+
+ name = 'regular_members'
+
+ @property
+ def members(self):
+ # Query for all the Members which have a role of MemberRole.member and
+ # are subscribed to this mailing list. Then return only those members
+ # that have a regular delivery mode.
+ for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.member):
+ if member.preferences.delivery_mode == DeliveryMode.regular:
+ yield member
+
+
+
+_digest_modes = (
+ DeliveryMode.mime_digests,
+ DeliveryMode.plaintext_digests,
+ DeliveryMode.summary_digests,
+ )
+
+
+
+class DigestMemberRoster(AbstractRoster):
+ """Return all the regular delivery members of a list."""
+
+ name = 'regular_members'
- def create(self, email_address, real_name=None):
- """See IRoster"""
- from Mailman.database.model.address import Address
- addr = Address.get_by(address=email_address)
- if addr:
- raise ExistingAddressError(email_address)
- addr = Address(address=email_address, real_name=real_name)
- # Make sure all the expected links are made, including to the null
- # (i.e. everyone) roster.
- self.addresses.append(addr)
- addr.rosters.append(self)
- null_roster = Roster.get_by(name='')
- null_roster.addresses.append(addr)
- addr.rosters.append(null_roster)
- return addr
+ @property
+ def members(self):
+ # Query for all the Members which have a role of MemberRole.member and
+ # are subscribed to this mailing list. Then return only those members
+ # that have one of the digest delivery modes.
+ for member in Member.select_by(mailing_list=self._mlist.fqdn_listname,
+ role=MemberRole.member):
+ if member.preferences.delivery_mode in _digest_modes:
+ yield member