summaryrefslogtreecommitdiff
path: root/src/mailman/model
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailman/model')
-rw-r--r--src/mailman/model/address.py22
-rw-r--r--src/mailman/model/autorespond.py5
-rw-r--r--src/mailman/model/digests.py2
-rw-r--r--src/mailman/model/docs/addresses.txt138
-rw-r--r--src/mailman/model/docs/autorespond.txt14
-rw-r--r--src/mailman/model/docs/domains.txt8
-rw-r--r--src/mailman/model/docs/languages.txt2
-rw-r--r--src/mailman/model/docs/listmanager.txt10
-rw-r--r--src/mailman/model/docs/mailinglist.txt5
-rw-r--r--src/mailman/model/docs/membership.txt293
-rw-r--r--src/mailman/model/docs/messagestore.txt30
-rw-r--r--src/mailman/model/docs/mlist-addresses.txt7
-rw-r--r--src/mailman/model/docs/pending.txt14
-rw-r--r--src/mailman/model/docs/registration.txt44
-rw-r--r--src/mailman/model/docs/requests.txt24
-rw-r--r--src/mailman/model/docs/usermanager.txt81
-rw-r--r--src/mailman/model/docs/users.txt59
-rw-r--r--src/mailman/model/domain.py8
-rw-r--r--src/mailman/model/language.py4
-rw-r--r--src/mailman/model/listmanager.py2
-rw-r--r--src/mailman/model/mailinglist.py49
-rw-r--r--src/mailman/model/member.py24
-rw-r--r--src/mailman/model/message.py4
-rw-r--r--src/mailman/model/messagestore.py2
-rw-r--r--src/mailman/model/mime.py4
-rw-r--r--src/mailman/model/pending.py6
-rw-r--r--src/mailman/model/preferences.py5
-rw-r--r--src/mailman/model/requests.py4
-rw-r--r--src/mailman/model/roster.py17
-rw-r--r--src/mailman/model/user.py26
-rw-r--r--src/mailman/model/usermanager.py39
-rw-r--r--src/mailman/model/version.py4
32 files changed, 532 insertions, 424 deletions
diff --git a/src/mailman/model/address.py b/src/mailman/model/address.py
index 6209858a8..6fa310c48 100644
--- a/src/mailman/model/address.py
+++ b/src/mailman/model/address.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -41,7 +41,7 @@ class Address(Model):
implements(IAddress)
id = Int(primary=True)
- address = Unicode()
+ email = Unicode()
_original = Unicode()
real_name = Unicode()
verified_on = DateTime()
@@ -52,15 +52,15 @@ class Address(Model):
preferences_id = Int()
preferences = Reference(preferences_id, 'Preferences.id')
- def __init__(self, address, real_name):
+ def __init__(self, email, real_name):
super(Address, self).__init__()
- lower_case = address.lower()
- self.address = lower_case
+ lower_case = email.lower()
+ self.email = lower_case
self.real_name = real_name
- self._original = (None if lower_case == address else address)
+ self._original = (None if lower_case == email else email)
def __str__(self):
- addr = (self.address if self._original is None else self._original)
+ addr = (self.email if self._original is None else self._original)
return formataddr((self.real_name, addr))
def __repr__(self):
@@ -71,7 +71,7 @@ class Address(Model):
address_str, verified, id(self))
else:
return '<Address: {0} [{1}] key: {2} at {3:#x}>'.format(
- address_str, verified, self.address, id(self))
+ address_str, verified, self.email, id(self))
def subscribe(self, mailing_list, role):
# This member has no preferences by default.
@@ -83,7 +83,7 @@ class Address(Model):
Member.address == self).one()
if member:
raise AlreadySubscribedError(
- mailing_list.fqdn_listname, self.address, role)
+ mailing_list.fqdn_listname, self.email, role)
member = Member(role=role,
mailing_list=mailing_list.fqdn_listname,
address=self)
@@ -92,5 +92,5 @@ class Address(Model):
return member
@property
- def original_address(self):
- return (self.address if self._original is None else self._original)
+ def original_email(self):
+ return (self.email if self._original is None else self._original)
diff --git a/src/mailman/model/autorespond.py b/src/mailman/model/autorespond.py
index 79eedd34a..8097fb013 100644
--- a/src/mailman/model/autorespond.py
+++ b/src/mailman/model/autorespond.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2009-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -33,8 +33,7 @@ from mailman.config import config
from mailman.database.model import Model
from mailman.database.types import Enum
from mailman.interfaces.autorespond import (
- IAutoResponseRecord, IAutoResponseSet, Response)
-from mailman.interfaces.mailinglist import IMailingList
+ IAutoResponseRecord, IAutoResponseSet)
from mailman.utilities.datetime import today
diff --git a/src/mailman/model/digests.py b/src/mailman/model/digests.py
index e3e6a89f9..ded0fe44f 100644
--- a/src/mailman/model/digests.py
+++ b/src/mailman/model/digests.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2009-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/src/mailman/model/docs/addresses.txt b/src/mailman/model/docs/addresses.txt
index 5388a3cc8..0ddacb321 100644
--- a/src/mailman/model/docs/addresses.txt
+++ b/src/mailman/model/docs/addresses.txt
@@ -18,42 +18,45 @@ Creating addresses
Addresses are created directly through the user manager, which starts out with
no addresses.
- >>> sorted(address.address for address in user_manager.addresses)
- []
+ >>> dump_list(address.email for address in user_manager.addresses)
+ *Empty*
Creating an unlinked email address is straightforward.
>>> address_1 = user_manager.create_address('aperson@example.com')
- >>> sorted(address.address for address in user_manager.addresses)
- [u'aperson@example.com']
+ >>> dump_list(address.email for address in user_manager.addresses)
+ aperson@example.com
However, such addresses have no real name.
- >>> address_1.real_name
- u''
+ >>> print address_1.real_name
+ <BLANKLINE>
You can also create an email address object with a real name.
>>> address_2 = user_manager.create_address(
... 'bperson@example.com', 'Ben Person')
- >>> sorted(address.address for address in user_manager.addresses)
- [u'aperson@example.com', u'bperson@example.com']
- >>> sorted(address.real_name for address in user_manager.addresses)
- [u'', u'Ben Person']
+ >>> dump_list(address.email for address in user_manager.addresses)
+ aperson@example.com
+ bperson@example.com
+ >>> dump_list(address.real_name for address in user_manager.addresses)
+ <BLANKLINE>
+ Ben Person
-The str() of the address is the RFC 2822 preferred originator format, while
-the repr() carries more information.
+The ``str()`` of the address is the RFC 2822 preferred originator format,
+while the ``repr()`` carries more information.
- >>> str(address_2)
- 'Ben Person <bperson@example.com>'
- >>> repr(address_2)
- '<Address: Ben Person <bperson@example.com> [not verified] at 0x...>'
+ >>> print str(address_2)
+ Ben Person <bperson@example.com>
+ >>> print repr(address_2)
+ <Address: Ben Person <bperson@example.com> [not verified] at 0x...>
You can assign real names to existing addresses.
>>> address_1.real_name = 'Anne Person'
- >>> sorted(address.real_name for address in user_manager.addresses)
- [u'Anne Person', u'Ben Person']
+ >>> dump_list(address.real_name for address in user_manager.addresses)
+ Anne Person
+ Ben Person
These addresses are not linked to users, and can be seen by searching the user
manager for an associated user.
@@ -68,12 +71,16 @@ interface.
>>> user_1 = user_manager.create_user(
... 'cperson@example.com', u'Claire Person')
- >>> sorted(address.address for address in user_1.addresses)
- [u'cperson@example.com']
- >>> sorted(address.address for address in user_manager.addresses)
- [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com']
- >>> sorted(address.real_name for address in user_manager.addresses)
- [u'Anne Person', u'Ben Person', u'Claire Person']
+ >>> dump_list(address.email for address in user_1.addresses)
+ cperson@example.com
+ >>> dump_list(address.email for address in user_manager.addresses)
+ aperson@example.com
+ bperson@example.com
+ cperson@example.com
+ >>> dump_list(address.real_name for address in user_manager.addresses)
+ Anne Person
+ Ben Person
+ Claire Person
And now you can find the associated user.
@@ -91,26 +98,28 @@ Deleting addresses
You can remove an unlinked address from the user manager.
>>> user_manager.delete_address(address_1)
- >>> sorted(address.address for address in user_manager.addresses)
- [u'bperson@example.com', u'cperson@example.com']
- >>> sorted(address.real_name for address in user_manager.addresses)
- [u'Ben Person', u'Claire Person']
+ >>> dump_list(address.email for address in user_manager.addresses)
+ bperson@example.com
+ cperson@example.com
+ >>> dump_list(address.real_name for address in user_manager.addresses)
+ Ben Person
+ Claire Person
Deleting a linked address does not delete the user, but it does unlink the
address from the user.
- >>> sorted(address.address for address in user_1.addresses)
- [u'cperson@example.com']
+ >>> dump_list(address.email for address in user_1.addresses)
+ cperson@example.com
>>> user_1.controls('cperson@example.com')
True
>>> address_3 = list(user_1.addresses)[0]
>>> user_manager.delete_address(address_3)
- >>> sorted(address.address for address in user_1.addresses)
- []
+ >>> dump_list(address.email for address in user_1.addresses)
+ *Empty*
>>> user_1.controls('cperson@example.com')
False
- >>> sorted(address.address for address in user_manager.addresses)
- [u'bperson@example.com']
+ >>> dump_list(address.email for address in user_manager.addresses)
+ bperson@example.com
Registration and validation
@@ -149,37 +158,40 @@ Subscriptions
Addresses get subscribed to mailing lists, not users. When the address is
subscribed, a role is specified.
+::
>>> address_5 = user_manager.create_address(
... 'eperson@example.com', 'Elly Person')
- >>> mlist = create_list('_xtext@example.com')
+ >>> mlist = create_list('test@example.com')
>>> from mailman.interfaces.member import MemberRole
>>> address_5.subscribe(mlist, MemberRole.owner)
<Member: Elly Person <eperson@example.com> on
- _xtext@example.com as MemberRole.owner>
+ test@example.com as MemberRole.owner>
>>> address_5.subscribe(mlist, MemberRole.member)
<Member: Elly Person <eperson@example.com> on
- _xtext@example.com as MemberRole.member>
+ test@example.com as MemberRole.member>
Now Elly is both an owner and a member of the mailing list.
- >>> sorted(mlist.owners.members)
- [<Member: Elly Person <eperson@example.com> on
- _xtext@example.com as MemberRole.owner>]
- >>> sorted(mlist.moderators.members)
- []
- >>> sorted(mlist.administrators.members)
- [<Member: Elly Person <eperson@example.com> on
- _xtext@example.com as MemberRole.owner>]
- >>> sorted(mlist.members.members)
- [<Member: Elly Person <eperson@example.com> on
- _xtext@example.com as MemberRole.member>]
- >>> sorted(mlist.regular_members.members)
- [<Member: Elly Person <eperson@example.com> on
- _xtext@example.com as MemberRole.member>]
- >>> sorted(mlist.digest_members.members)
- []
+ >>> def memberkey(member):
+ ... return member.mailing_list, member.address.email, int(member.role)
+ >>> dump_list(mlist.owners.members, key=memberkey)
+ <Member: Elly Person <eperson@example.com> on
+ test@example.com as MemberRole.owner>
+ >>> dump_list(mlist.moderators.members, key=memberkey)
+ *Empty*
+ >>> dump_list(mlist.administrators.members, key=memberkey)
+ <Member: Elly Person <eperson@example.com> on
+ test@example.com as MemberRole.owner>
+ >>> dump_list(mlist.members.members, key=memberkey)
+ <Member: Elly Person <eperson@example.com> on
+ test@example.com as MemberRole.member>
+ >>> dump_list(mlist.regular_members.members, key=memberkey)
+ <Member: Elly Person <eperson@example.com> on
+ test@example.com as MemberRole.member>
+ >>> dump_list(mlist.digest_members.members, key=memberkey)
+ *Empty*
Case-preserved addresses
@@ -197,18 +209,18 @@ The str() of such an address prints the RFC 2822 preferred originator format
with the original case-preserved address. The repr() contains all the gory
details.
- >>> str(address_6)
- 'Frank Person <FPERSON@example.com>'
- >>> repr(address_6)
- '<Address: Frank Person <FPERSON@example.com> [not verified]
- key: fperson@example.com at 0x...>'
+ >>> print str(address_6)
+ Frank Person <FPERSON@example.com>
+ >>> print repr(address_6)
+ <Address: Frank Person <FPERSON@example.com> [not verified]
+ key: fperson@example.com at 0x...>
Both the case-insensitive version of the address and the original
-case-preserved version are available on attributes of the IAddress object.
+case-preserved version are available on attributes of the `IAddress` object.
- >>> print address_6.address
+ >>> print address_6.email
fperson@example.com
- >>> print address_6.original_address
+ >>> print address_6.original_email
FPERSON@example.com
Because addresses are case-insensitive for all other purposes, you cannot
@@ -230,7 +242,7 @@ create an address that differs only in case.
You can get the address using either the lower cased version or case-preserved
version. In fact, searching for an address is case insensitive.
- >>> print user_manager.get_address('fperson@example.com').address
+ >>> print user_manager.get_address('fperson@example.com').email
fperson@example.com
- >>> print user_manager.get_address('FPERSON@example.com').address
+ >>> print user_manager.get_address('FPERSON@example.com').email
fperson@example.com
diff --git a/src/mailman/model/docs/autorespond.txt b/src/mailman/model/docs/autorespond.txt
index ba0521a89..3a9ad01b2 100644
--- a/src/mailman/model/docs/autorespond.txt
+++ b/src/mailman/model/docs/autorespond.txt
@@ -3,11 +3,12 @@ Automatic responder
===================
In various situations, Mailman will send an automatic response to the author
-of an email message. For example, if someone sends a command to the -request
-address, Mailman will send a response, but to cut down on third party spam,
-the sender will only get a certain number of responses per day.
+of an email message. For example, if someone sends a command to the
+``-request`` address, Mailman will send a response, but to cut down on third
+party spam, the sender will only get a certain number of responses per day.
-First, given a mailing list you need to adapt it to an IAutoResponseSet.
+First, given a mailing list you need to adapt it to an ``IAutoResponseSet``.
+::
>>> mlist = create_list('test@example.com')
>>> from mailman.interfaces.autorespond import IAutoResponseSet
@@ -17,7 +18,7 @@ First, given a mailing list you need to adapt it to an IAutoResponseSet.
>>> verifyObject(IAutoResponseSet, response_set)
True
-You can't adapt other objects to an IAutoResponseSet.
+You can't adapt other objects to an ``IAutoResponseSet``.
>>> IAutoResponseSet(object())
Traceback (most recent call last):
@@ -28,6 +29,7 @@ There are various kinds of response types. For example, Mailman will send an
automatic response when messages are held for approval, or when it receives an
email command. You can find out how many responses for a particular address
have already been sent today.
+::
>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
@@ -66,6 +68,7 @@ Let's send one more.
2
Now the day flips over and all the counts reset.
+::
>>> from mailman.utilities.datetime import factory
>>> factory.fast_forward()
@@ -92,6 +95,7 @@ You can also use the response set to get the date of the last response sent.
datetime.date(2005, 8, 1)
When another response is sent today, that becomes the last one sent.
+::
>>> response_set.response_sent(address, Response.command)
>>> response_set.last_response(address, Response.command).date_sent
diff --git a/src/mailman/model/docs/domains.txt b/src/mailman/model/docs/domains.txt
index 5673e6ee9..9fe43a5f1 100644
--- a/src/mailman/model/docs/domains.txt
+++ b/src/mailman/model/docs/domains.txt
@@ -2,7 +2,7 @@
Domains
=======
- # The test framework starts out with an example domain, so let's delete
+.. # The test framework starts out with an example domain, so let's delete
# that first.
>>> from mailman.interfaces.domain import IDomainManager
>>> from zope.component import getUtility
@@ -11,6 +11,7 @@ Domains
<Domain example.com...>
Domains are how Mailman interacts with email host names and web host names.
+::
>>> from operator import attrgetter
>>> def show_domains():
@@ -52,6 +53,7 @@ web interface for the domain.
contact_address: postmaster@example.com>
Domains can have explicit descriptions and contact addresses.
+::
>>> manager.add(
... 'example.net',
@@ -70,6 +72,7 @@ Domains can have explicit descriptions and contact addresses.
contact_address: postmaster@example.com>
In the global domain manager, domains are indexed by their email host name.
+::
>>> for domain in sorted(manager, key=attrgetter('email_host')):
... print domain.email_host
@@ -87,7 +90,8 @@ In the global domain manager, domains are indexed by their email host name.
KeyError: u'doesnotexist.com'
As with a dictionary, you can also get the domain. If the domain does not
-exist, None or a default is returned.
+exist, ``None`` or a default is returned.
+::
>>> print manager.get('example.net')
<Domain example.net, The example domain,
diff --git a/src/mailman/model/docs/languages.txt b/src/mailman/model/docs/languages.txt
index a724a0510..21143f28b 100644
--- a/src/mailman/model/docs/languages.txt
+++ b/src/mailman/model/docs/languages.txt
@@ -5,6 +5,7 @@ Languages
Mailman is multilingual. A language manager handles the known set of
languages at run time, as well as enabling those languages for use in a
running Mailman instance.
+::
>>> from mailman.interfaces.languages import ILanguageManager
>>> from zope.component import getUtility
@@ -94,6 +95,7 @@ Clearing the known languages
============================
The language manager can forget about all the language codes it knows about.
+::
>>> 'en' in mgr
True
diff --git a/src/mailman/model/docs/listmanager.txt b/src/mailman/model/docs/listmanager.txt
index e07659066..7235049c7 100644
--- a/src/mailman/model/docs/listmanager.txt
+++ b/src/mailman/model/docs/listmanager.txt
@@ -2,10 +2,8 @@
The mailing list manager
========================
-The IListManager is how you create, delete, and retrieve mailing list
-objects. The Mailman system instantiates an IListManager for you based on the
-configuration variable MANAGERS_INIT_FUNCTION. The instance is accessible
-on the global config object.
+The ``IListManager`` is how you create, delete, and retrieve mailing list
+objects.
>>> from mailman.interfaces.listmanager import IListManager
>>> from zope.component import getUtility
@@ -76,12 +74,12 @@ always get the same object back.
>>> mlist_2 is mlist
True
-If you try to get a list that doesn't existing yet, you get None.
+If you try to get a list that doesn't existing yet, you get ``None``.
>>> print list_manager.get('_xtest_2@example.com')
None
-You also get None if the list name is invalid.
+You also get ``None`` if the list name is invalid.
>>> print list_manager.get('foo')
None
diff --git a/src/mailman/model/docs/mailinglist.txt b/src/mailman/model/docs/mailinglist.txt
index 687f7b39c..33d681762 100644
--- a/src/mailman/model/docs/mailinglist.txt
+++ b/src/mailman/model/docs/mailinglist.txt
@@ -2,7 +2,7 @@
Mailing lists
=============
-XXX 2010-06-18 BAW: This documentation needs a lot more detail.
+.. XXX 2010-06-18 BAW: This documentation needs a lot more detail.
The mailing list is a core object in Mailman. It is uniquely identified in
the system by its posting address, i.e. the email address you would send a
@@ -25,10 +25,11 @@ name (i.e. local part) and host name.
Rosters
=======
-Mailing list membership is represented by 'rosters'. Each mailing list has
+Mailing list membership is represented by `rosters`. Each mailing list has
several rosters of members, representing the subscribers to the mailing list,
the owners, the moderators, and so on. The rosters are defined by a
membership role.
+::
>>> from mailman.interfaces.member import MemberRole
>>> from mailman.testing.helpers import subscribe
diff --git a/src/mailman/model/docs/membership.txt b/src/mailman/model/docs/membership.txt
index 41660f0d2..00e79d733 100644
--- a/src/mailman/model/docs/membership.txt
+++ b/src/mailman/model/docs/membership.txt
@@ -4,159 +4,116 @@ List memberships
Users represent people in Mailman. Users control email addresses, and rosters
are collections of members. A member gives an email address a role, such as
-'member', 'administrator', or 'moderator'. Roster sets are collections of
-rosters and a mailing list has a single roster set that contains all its
-members, regardless of that member's role.
+`member`, `administrator`, or `moderator`. Even nonmembers are represented by
+a roster.
+
+Roster sets are collections of rosters and a mailing list has a single roster
+set that contains all its members, regardless of that member's role.
Mailing lists and roster sets have an indirect relationship, through the
roster set's name. Roster also have names, but are related to roster sets
by a more direct containment relationship. This is because it is possible to
store mailing list data in a different database than user data.
-When we create a mailing list, it starts out with no members...
-
- >>> mlist = create_list('_xtest@example.com')
- >>> mlist
- <mailing list "_xtest@example.com" at ...>
- >>> sorted(member.address.address for member in mlist.members.members)
- []
- >>> sorted(user.real_name for user in mlist.members.users)
- []
- >>> sorted(address.address for member in mlist.members.addresses)
- []
-
-...no owners...
-
- >>> sorted(member.address.address for member in mlist.owners.members)
- []
- >>> sorted(user.real_name for user in mlist.owners.users)
- []
- >>> sorted(address.address for member in mlist.owners.addresses)
- []
-
-...no moderators...
-
- >>> sorted(member.address.address for member in mlist.moderators.members)
- []
- >>> sorted(user.real_name for user in mlist.moderators.users)
- []
- >>> sorted(address.address for member in mlist.moderators.addresses)
- []
+When we create a mailing list, it starts out with no members, owners,
+moderators, administrators, or nonmembers.
-...and no administrators.
-
- >>> sorted(member.address.address
- ... for member in mlist.administrators.members)
- []
- >>> sorted(user.real_name for user in mlist.administrators.users)
- []
- >>> sorted(address.address for member in mlist.administrators.addresses)
- []
+ >>> mlist = create_list('test@example.com')
+ >>> dump_list(mlist.members.members)
+ *Empty*
+ >>> dump_list(mlist.owners.members)
+ *Empty*
+ >>> dump_list(mlist.moderators.members)
+ *Empty*
+ >>> dump_list(mlist.administrators.members)
+ *Empty*
+ >>> dump_list(mlist.nonmembers.members)
+ *Empty*
Administrators
==============
A mailing list's administrators are defined as union of the list's owners and
-the list's moderators. We can add new owners or moderators to this list by
-assigning roles to users. First we have to create the user, because there are
-no users in the user database yet.
+moderators. We can add new owners or moderators to this list by assigning
+roles to users. First we have to create the user, because there are no users
+in the user database yet.
>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
>>> user_manager = getUtility(IUserManager)
>>> user_1 = user_manager.create_user('aperson@example.com', 'Anne Person')
- >>> print user_1.real_name
- Anne Person
- >>> sorted(address.address for address in user_1.addresses)
- [u'aperson@example.com']
+ >>> print user_1
+ <User "Anne Person" at ...>
We can add Anne as an owner of the mailing list, by creating a member role for
her.
>>> from mailman.interfaces.member import MemberRole
>>> address_1 = list(user_1.addresses)[0]
- >>> print address_1.address
- aperson@example.com
>>> address_1.subscribe(mlist, MemberRole.owner)
<Member: Anne Person <aperson@example.com> on
- _xtest@example.com as MemberRole.owner>
- >>> sorted(member.address.address for member in mlist.owners.members)
- [u'aperson@example.com']
- >>> sorted(user.real_name for user in mlist.owners.users)
- [u'Anne Person']
- >>> sorted(address.address for address in mlist.owners.addresses)
- [u'aperson@example.com']
+ test@example.com as MemberRole.owner>
+ >>> dump_list(member.address for member in mlist.owners.members)
+ Anne Person <aperson@example.com>
Adding Anne as a list owner also makes her an administrator, but does not make
her a moderator. Nor does it make her a member of the list.
- >>> sorted(user.real_name for user in mlist.administrators.users)
- [u'Anne Person']
- >>> sorted(user.real_name for user in mlist.moderators.users)
- []
- >>> sorted(user.real_name for user in mlist.members.users)
- []
+ >>> dump_list(member.address for member in mlist.administrators.members)
+ Anne Person <aperson@example.com>
+ >>> dump_list(member.address for member in mlist.moderators.members)
+ *Empty*
+ >>> dump_list(member.address for member in mlist.members.members)
+ *Empty*
-We can add Ben as a moderator of the list, by creating a different member role
-for him.
+Bart becomes a moderator of the list.
- >>> user_2 = user_manager.create_user('bperson@example.com', 'Ben Person')
- >>> print user_2.real_name
- Ben Person
+ >>> user_2 = user_manager.create_user('bperson@example.com', 'Bart Person')
+ >>> print user_2
+ <User "Bart Person" at ...>
>>> address_2 = list(user_2.addresses)[0]
- >>> print address_2.address
- bperson@example.com
>>> address_2.subscribe(mlist, MemberRole.moderator)
- <Member: Ben Person <bperson@example.com>
- on _xtest@example.com as MemberRole.moderator>
- >>> sorted(member.address.address for member in mlist.moderators.members)
- [u'bperson@example.com']
- >>> sorted(user.real_name for user in mlist.moderators.users)
- [u'Ben Person']
- >>> sorted(address.address for address in mlist.moderators.addresses)
- [u'bperson@example.com']
+ <Member: Bart Person <bperson@example.com>
+ on test@example.com as MemberRole.moderator>
+ >>> dump_list(member.address for member in mlist.moderators.members)
+ Bart Person <bperson@example.com>
+
+Now, both Anne and Bart are list administrators.
+::
-Now, both Anne and Ben are list administrators.
+ >>> from operator import attrgetter
+ >>> def dump_members(roster):
+ ... all_addresses = list(member.address for member in roster)
+ ... sorted_addresses = sorted(all_addresses, key=attrgetter('email'))
+ ... dump_list(sorted_addresses)
- >>> sorted(member.address.address
- ... for member in mlist.administrators.members)
- [u'aperson@example.com', u'bperson@example.com']
- >>> sorted(user.real_name for user in mlist.administrators.users)
- [u'Anne Person', u'Ben Person']
- >>> sorted(address.address for address in mlist.administrators.addresses)
- [u'aperson@example.com', u'bperson@example.com']
+ >>> dump_members(mlist.administrators.members)
+ Anne Person <aperson@example.com>
+ Bart Person <bperson@example.com>
Members
=======
-Similarly, list members are born of users being given the proper role. It's
-more interesting here because these roles should have a preference which can
-be used to decide whether the member is to get regular delivery or digest
-delivery. Without a preference, Mailman will fall back first to the address's
-preference, then the user's preference, then the list's preference. Start
-without any member preference to see the system defaults.
+Similarly, list members are born of users being subscribed with the proper
+role.
>>> user_3 = user_manager.create_user(
- ... 'cperson@example.com', 'Claire Person')
- >>> print user_3.real_name
- Claire Person
+ ... 'cperson@example.com', 'Cris Person')
>>> address_3 = list(user_3.addresses)[0]
- >>> print address_3.address
- cperson@example.com
>>> address_3.subscribe(mlist, MemberRole.member)
- <Member: Claire Person <cperson@example.com>
- on _xtest@example.com as MemberRole.member>
+ <Member: Cris Person <cperson@example.com>
+ on test@example.com as MemberRole.member>
-Claire will be a regular delivery member but not a digest member.
+Cris will be a regular delivery member but not a digest member.
- >>> sorted(address.address for address in mlist.members.addresses)
- [u'cperson@example.com']
- >>> sorted(address.address for address in mlist.regular_members.addresses)
- [u'cperson@example.com']
- >>> sorted(address.address for address in mlist.digest_members.addresses)
- []
+ >>> dump_members(mlist.members.members)
+ Cris Person <cperson@example.com>
+ >>> dump_members(mlist.regular_members.members)
+ Cris Person <cperson@example.com>
+ >>> dump_members(mlist.digest_members.addresses)
+ *Empty*
It's easy to make the list administrators members of the mailing list too.
@@ -164,34 +121,75 @@ It's easy to make the list administrators members of the mailing list too.
>>> for address in mlist.administrators.addresses:
... member = address.subscribe(mlist, MemberRole.member)
... members.append(member)
- >>> sorted(members, key=lambda m: m.address.address)
- [<Member: Anne Person <aperson@example.com> on
- _xtest@example.com as MemberRole.member>,
- <Member: Ben Person <bperson@example.com> on
- _xtest@example.com as MemberRole.member>]
- >>> sorted(address.address for address in mlist.members.addresses)
- [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com']
- >>> sorted(address.address for address in mlist.regular_members.addresses)
- [u'aperson@example.com', u'bperson@example.com', u'cperson@example.com']
- >>> sorted(address.address for address in mlist.digest_members.addresses)
- []
+ >>> dump_list(members, key=attrgetter('address.email'))
+ <Member: Anne Person <aperson@example.com> on
+ test@example.com as MemberRole.member>
+ <Member: Bart Person <bperson@example.com> on
+ test@example.com as MemberRole.member>
+ >>> dump_members(mlist.members.members)
+ Anne Person <aperson@example.com>
+ Bart Person <bperson@example.com>
+ Cris Person <cperson@example.com>
+ >>> dump_members(mlist.regular_members.members)
+ Anne Person <aperson@example.com>
+ Bart Person <bperson@example.com>
+ Cris Person <cperson@example.com>
+ >>> dump_members(mlist.digest_members.members)
+ *Empty*
+
+
+Nonmembers
+==========
+
+Nonmembers are used to represent people who have posted to the mailing list
+but are not subscribed to the mailing list. These may be legitimate users who
+have found the mailing list and wish to interact without a direct
+subscription, or they may be spammers who should never be allowed to contact
+the mailing list. Because all the same moderation rules can be applied to
+nonmembers, we represent them as the same type of object but with a different
+role.
+
+ >>> user_6 = user_manager.create_user('fperson@example.com', 'Fred Person')
+ >>> address_6 = list(user_6.addresses)[0]
+ >>> member_6 = address_6.subscribe(mlist, MemberRole.nonmember)
+ >>> member_6
+ <Member: Fred Person <fperson@example.com> on test@example.com
+ as MemberRole.nonmember>
+ >>> dump_members(mlist.nonmembers.members)
+ Fred Person <fperson@example.com>
+
+Nonmembers do not get delivery of any messages.
+
+ >>> dump_members(mlist.members.members)
+ Anne Person <aperson@example.com>
+ Bart Person <bperson@example.com>
+ Cris Person <cperson@example.com>
+ >>> dump_members(mlist.regular_members.members)
+ Anne Person <aperson@example.com>
+ Bart Person <bperson@example.com>
+ Cris Person <cperson@example.com>
+ >>> dump_members(mlist.digest_members.members)
+ *Empty*
Finding members
===============
-You can find the IMember object that is a member of a roster for a given text
-email address by using an IRoster's .get_member() method.
+You can find the ``IMember`` object that is a member of a roster for a given
+text email address by using the ``IRoster.get_member()`` method.
>>> mlist.owners.get_member('aperson@example.com')
<Member: Anne Person <aperson@example.com> on
- _xtest@example.com as MemberRole.owner>
+ test@example.com as MemberRole.owner>
>>> mlist.administrators.get_member('aperson@example.com')
<Member: Anne Person <aperson@example.com> on
- _xtest@example.com as MemberRole.owner>
+ test@example.com as MemberRole.owner>
>>> mlist.members.get_member('aperson@example.com')
<Member: Anne Person <aperson@example.com> on
- _xtest@example.com as MemberRole.member>
+ test@example.com as MemberRole.member>
+ >>> mlist.nonmembers.get_member('fperson@example.com')
+ <Member: Fred Person <fperson@example.com> on
+ test@example.com as MemberRole.nonmember>
However, if the address is not subscribed with the appropriate role, then None
is returned.
@@ -202,6 +200,8 @@ is returned.
None
>>> print mlist.members.get_member('zperson@example.com')
None
+ >>> print mlist.nonmembers.get_member('aperson@example.com')
+ None
All subscribers
@@ -211,14 +211,15 @@ There is also a roster containing all the subscribers of a mailing list,
regardless of their role.
>>> def sortkey(member):
- ... return (member.address.address, int(member.role))
- >>> [(member.address.address, str(member.role))
- ... for member in sorted(mlist.subscribers.members, key=sortkey)]
- [(u'aperson@example.com', 'MemberRole.member'),
- (u'aperson@example.com', 'MemberRole.owner'),
- (u'bperson@example.com', 'MemberRole.member'),
- (u'bperson@example.com', 'MemberRole.moderator'),
- (u'cperson@example.com', 'MemberRole.member')]
+ ... return (member.address.email, int(member.role))
+ >>> for member in sorted(mlist.subscribers.members, key=sortkey):
+ ... print member.address.email, member.role
+ aperson@example.com MemberRole.member
+ aperson@example.com MemberRole.owner
+ bperson@example.com MemberRole.member
+ bperson@example.com MemberRole.moderator
+ cperson@example.com MemberRole.member
+ fperson@example.com MemberRole.nonmember
Double subscriptions
@@ -230,4 +231,34 @@ It is an error to subscribe someone to a list with the same role twice.
Traceback (most recent call last):
...
AlreadySubscribedError: aperson@example.com is already a MemberRole.owner
- of mailing list _xtest@example.com
+ of mailing list test@example.com
+
+
+Moderation actions
+==================
+
+All members of any role have a *moderation action* which specifies how
+postings from that member are handled. By default, owners and moderators are
+automatically accepted for posting to the mailing list.
+
+ >>> for member in sorted(mlist.administrators.members,
+ ... key=attrgetter('address.email')):
+ ... print member.address.email, member.role, member.moderation_action
+ aperson@example.com MemberRole.owner Action.accept
+ bperson@example.com MemberRole.moderator Action.accept
+
+By default, members have a *deferred* action which specifies that the posting
+should go through the normal moderation checks.
+
+ >>> for member in sorted(mlist.members.members,
+ ... key=attrgetter('address.email')):
+ ... print member.address.email, member.role, member.moderation_action
+ aperson@example.com MemberRole.member Action.defer
+ bperson@example.com MemberRole.member Action.defer
+ cperson@example.com MemberRole.member Action.defer
+
+Postings by nonmembers are held for moderator approval by default.
+
+ >>> for member in mlist.nonmembers.members:
+ ... print member.address.email, member.role, member.moderation_action
+ fperson@example.com MemberRole.nonmember Action.hold
diff --git a/src/mailman/model/docs/messagestore.txt b/src/mailman/model/docs/messagestore.txt
index aabfd55fb..3ee59129b 100644
--- a/src/mailman/model/docs/messagestore.txt
+++ b/src/mailman/model/docs/messagestore.txt
@@ -2,17 +2,17 @@
The message store
=================
-The message store is a collection of messages keyed off of Message-ID and
-X-Message-ID-Hash headers. Either of these values can be combined with the
-message's List-Archive header to create a globally unique URI to the message
-object in the internet facing interface of the message store. The
-X-Message-ID-Hash is the Base32 SHA1 hash of the Message-ID.
+The message store is a collection of messages keyed off of ``Message-ID`` and
+``X-Message-ID-Hash`` headers. Either of these values can be combined with
+the message's ``List-Archive`` header to create a globally unique URI to the
+message object in the internet facing interface of the message store. The
+``X-Message-ID-Hash`` is the Base32 SHA1 hash of the ``Message-ID``.
>>> from mailman.interfaces.messages import IMessageStore
>>> from zope.component import getUtility
>>> message_store = getUtility(IMessageStore)
-If you try to add a message to the store which is missing the Message-ID
+If you try to add a message to the store which is missing the ``Message-ID``
header, you will get an exception.
>>> msg = message_from_string("""\
@@ -25,7 +25,7 @@ header, you will get an exception.
...
ValueError: Exactly one Message-ID header required
-However, if the message has a Message-ID header, it can be stored.
+However, if the message has a ``Message-ID`` header, it can be stored.
>>> msg['Message-ID'] = '<87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp>'
>>> message_store.add(msg)
@@ -42,16 +42,16 @@ However, if the message has a Message-ID header, it can be stored.
Finding messages
================
-There are several ways to find a message given either the Message-ID or
-X-Message-ID-Hash headers. In either case, if no matching message is found,
-None is returned.
+There are several ways to find a message given either the ``Message-ID`` or
+``X-Message-ID-Hash`` headers. In either case, if no matching message is
+found, ``None`` is returned.
>>> print message_store.get_message_by_id('nothing')
None
>>> print message_store.get_message_by_hash('nothing')
None
-Given an existing Message-ID, the message can be found.
+Given an existing ``Message-ID``, the message can be found.
>>> message = message_store.get_message_by_id(msg['message-id'])
>>> print message.as_string()
@@ -62,7 +62,7 @@ Given an existing Message-ID, the message can be found.
This message is very important.
<BLANKLINE>
-Similarly, we can find messages by the X-Message-ID-Hash:
+Similarly, we can find messages by the ``X-Message-ID-Hash``:
>>> message = message_store.get_message_by_hash(msg['x-message-id-hash'])
>>> print message.as_string()
@@ -95,9 +95,9 @@ contains.
Deleting messages from the store
================================
-You delete a message from the storage service by providing the Message-ID for
-the message you want to delete. If you try to delete a Message-ID that isn't
-in the store, you get an exception.
+You delete a message from the storage service by providing the ``Message-ID``
+for the message you want to delete. If you try to delete a ``Message-ID``
+that isn't in the store, you get an exception.
>>> message_store.delete_message('nothing')
Traceback (most recent call last):
diff --git a/src/mailman/model/docs/mlist-addresses.txt b/src/mailman/model/docs/mlist-addresses.txt
index 3f44008fb..2a021f67f 100644
--- a/src/mailman/model/docs/mlist-addresses.txt
+++ b/src/mailman/model/docs/mlist-addresses.txt
@@ -3,7 +3,7 @@ Mailing list addresses
======================
Every mailing list has a number of addresses which are publicly available.
-These are defined in the IMailingListAddresses interface.
+These are defined in the ``IMailingListAddresses`` interface.
>>> mlist = create_list('_xtest@example.com')
@@ -15,7 +15,7 @@ list. This is exactly the same as the fully qualified list name.
>>> print mlist.posting_address
_xtest@example.com
-Messages to the mailing list's 'no reply' address always get discarded without
+Messages to the mailing list's `no reply` address always get discarded without
prejudice.
>>> print mlist.no_reply_address
@@ -61,7 +61,8 @@ Email confirmations
Email confirmation messages are sent when actions such as subscriptions need
to be confirmed. It requires that a cookie be provided, which will be
included in the local part of the email address. The exact format of this is
-dependent on the VERP_CONFIRM_FORMAT configuration variable.
+dependent on the ``verp_confirm_format`` configuration variable.
+::
>>> print mlist.confirm_address('cookie')
_xtest-confirm+cookie@example.com
diff --git a/src/mailman/model/docs/pending.txt b/src/mailman/model/docs/pending.txt
index dc27b6bee..e85d8e484 100644
--- a/src/mailman/model/docs/pending.txt
+++ b/src/mailman/model/docs/pending.txt
@@ -17,8 +17,8 @@ available by adapting the list manager.
>>> from zope.component import getUtility
>>> pendingdb = getUtility(IPendings)
-The pending database can add any IPendable to the database, returning a token
-that can be used in urls and such.
+The pending database can add any ``IPendable`` to the database, returning a
+token that can be used in urls and such.
>>> from mailman.interfaces.pending import IPendable
>>> class SimplePendable(dict):
@@ -33,10 +33,10 @@ that can be used in urls and such.
>>> len(token)
40
-There's not much you can do with tokens except to 'confirm' them, which
-basically means returning the IPendable structure (as a dict) from the
-database that matches the token. If the token isn't in the database, None is
-returned.
+There's not much you can do with tokens except to `confirm` them, which
+basically means returning the ``IPendable`` structure (as a dictionary) from
+the database that matches the token. If the token isn't in the database,
+``None`` is returned.
>>> pendable = pendingdb.confirm(bytes('missing'))
>>> print pendable
@@ -56,7 +56,7 @@ After confirmation, the token is no longer in the database.
None
There are a few other things you can do with the pending database. When you
-confirm a token, you can leave it in the database, or in otherwords, not
+confirm a token, you can leave it in the database, or in other words, not
expunge it.
>>> event_1 = SimplePendable(type='one')
diff --git a/src/mailman/model/docs/registration.txt b/src/mailman/model/docs/registration.txt
index abc7f2c93..e92c63f52 100644
--- a/src/mailman/model/docs/registration.txt
+++ b/src/mailman/model/docs/registration.txt
@@ -7,10 +7,10 @@ The only thing they must supply is an email address, although there is
additional information they may supply. All registered email addresses must
be verified before Mailman will send them any list traffic.
-The IUserManager manages users, but it does so at a fairly low level.
+The ``IUserManager`` manages users, but it does so at a fairly low level.
Specifically, it does not handle verifications, email address syntax validity
-checks, etc. The IRegistrar is the interface to the object handling all this
-stuff.
+checks, etc. The ``IRegistrar`` is the interface to the object handling all
+this stuff.
>>> from mailman.interfaces.registrar import IRegistrar
>>> from zope.component import getUtility
@@ -76,9 +76,9 @@ Register an email address
=========================
Registration of an unknown address creates nothing until the confirmation step
-is complete. No IUser or IAddress is created at registration time, but a
-record is added to the pending database, and the token for that record is
-returned.
+is complete. No ``IUser`` or ``IAddress`` is created at registration time,
+but a record is added to the pending database, and the token for that record
+is returned.
>>> token = registrar.register(mlist, 'aperson@example.com', 'Anne Person')
>>> check_token(token)
@@ -100,7 +100,7 @@ But this address is waiting for confirmation.
>>> pendingdb = getUtility(IPendings)
>>> dump_msgdata(pendingdb.confirm(token, expunge=False))
- address : aperson@example.com
+ email : aperson@example.com
list_name: alpha@example.com
real_name: Anne Person
type : registration
@@ -161,12 +161,12 @@ appear in a URL in the body of the message.
>>> sent_token == token
True
-The same token will appear in the From header.
+The same token will appear in the ``From`` header.
>>> items[0].msg['from'] == 'alpha-confirm+' + token + '@example.com'
True
-It will also appear in the Subject header.
+It will also appear in the ``Subject`` header.
>>> items[0].msg['subject'] == 'confirm ' + token
True
@@ -178,8 +178,8 @@ token and uses that to confirm the pending registration.
>>> registrar.confirm(token)
True
-Now, there is an IAddress in the database matching the address, as well as an
-IUser linked to this address. The IAddress is verified.
+Now, there is an `IAddress` in the database matching the address, as well as
+an `IUser` linked to this address. The `IAddress` is verified.
>>> found_address = user_manager.get_address('aperson@example.com')
>>> found_address
@@ -187,7 +187,7 @@ IUser linked to this address. The IAddress is verified.
>>> found_user = user_manager.get_user('aperson@example.com')
>>> found_user
<User "Anne Person" at ...>
- >>> found_user.controls(found_address.address)
+ >>> found_user.controls(found_address.email)
True
>>> from datetime import datetime
>>> isinstance(found_address.verified_on, datetime)
@@ -247,7 +247,8 @@ Discarding
==========
A confirmation token can also be discarded, say if the user changes his or her
-mind about registering. When discarded, no IAddress or IUser is created.
+mind about registering. When discarded, no `IAddress` or `IUser` is created.
+::
>>> token = registrar.register(mlist, 'eperson@example.com', 'Elly Person')
>>> check_token(token)
@@ -270,6 +271,7 @@ Registering a new address for an existing user
When a new address for an existing user is registered, there isn't too much
different except that the new address will still need to be verified before it
can be used.
+::
>>> dperson = user_manager.create_user(
... 'dperson@example.com', 'Dave Person')
@@ -279,8 +281,8 @@ can be used.
>>> address.verified_on = datetime.now()
>>> from operator import attrgetter
- >>> sorted((addr for addr in dperson.addresses), key=attrgetter('address'))
- [<Address: Dave Person <dperson@example.com> [verified] at ...>]
+ >>> dump_list(repr(address) for address in dperson.addresses)
+ <Address: Dave Person <dperson@example.com> [verified] at ...>
>>> dperson.register('david.person@example.com', 'David Person')
<Address: David Person <david.person@example.com> [not verified] at ...>
>>> token = registrar.register(mlist, 'david.person@example.com')
@@ -296,9 +298,9 @@ can be used.
True
>>> user
<User "Dave Person" at ...>
- >>> sorted((addr for addr in user.addresses), key=attrgetter('address'))
- [<Address: David Person <david.person@example.com> [verified] at ...>,
- <Address: Dave Person <dperson@example.com> [verified] at ...>]
+ >>> dump_list(repr(address) for address in user.addresses)
+ <Address: Dave Person <dperson@example.com> [verified] at ...>
+ <Address: David Person <david.person@example.com> [verified] at ...>
Corner cases
@@ -310,9 +312,9 @@ confirm method will just return False.
>>> registrar.confirm(bytes('no token'))
False
-Likewise, if you try to confirm, through the IUserRegistrar interface, a token
-that doesn't match a registration event, you will get None. However, the
-pending event matched with that token will still be removed.
+Likewise, if you try to confirm, through the `IUserRegistrar` interface, a
+token that doesn't match a registration event, you will get ``None``.
+However, the pending event matched with that token will still be removed.
>>> from mailman.interfaces.pending import IPendable
>>> from zope.interface import implements
diff --git a/src/mailman/model/docs/requests.txt b/src/mailman/model/docs/requests.txt
index 8cd027297..94c81e1dc 100644
--- a/src/mailman/model/docs/requests.txt
+++ b/src/mailman/model/docs/requests.txt
@@ -35,6 +35,7 @@ Mailing list centric
A set of requests are always related to a particular mailing list, so given a
mailing list you need to get its requests object.
+::
>>> from mailman.interfaces.requests import IListRequests, IRequests
>>> from zope.component import getUtility
@@ -55,8 +56,8 @@ The list's requests database starts out empty.
>>> requests.count
0
- >>> list(requests.held_requests)
- []
+ >>> dump_list(requests.held_requests)
+ *Empty*
At the lowest level, the requests database is very simple. Holding a request
requires a request type (as an enum value), a key, and an optional dictionary
@@ -203,10 +204,10 @@ For the next section, we first clean up all the current requests.
Application support
===================
-There are several higher level interfaces available in the mailman.app package
-which can be used to hold messages, subscription, and unsubscriptions. There
-are also interfaces for disposing of these requests in an application specific
-and consistent way.
+There are several higher level interfaces available in the ``mailman.app``
+package which can be used to hold messages, subscription, and unsubscriptions.
+There are also interfaces for disposing of these requests in an application
+specific and consistent way.
>>> from mailman.app import moderator
@@ -237,6 +238,7 @@ this case, we won't include any additional metadata.
True
We can also hold a message with some additional metadata.
+::
# Delete the Message-ID from the previous hold so we don't try to store
# collisions in the message storage.
@@ -336,6 +338,7 @@ re-added to the message store). When handling a message, we can tell the
moderator interface to also preserve a copy, essentially telling it not to
delete the message from the storage. First, without the switch, the message
is deleted.
+::
>>> msg = message_from_string("""\
... From: aperson@example.org
@@ -374,6 +377,7 @@ the message store after disposition.
Orthogonal to preservation, the message can also be forwarded to another
address. This is helpful for getting the message into the inbox of one of the
moderators.
+::
# Set a new Message-ID from the previous hold so we don't try to store
# collisions in the message storage.
@@ -678,6 +682,7 @@ The admin message is sent to the moderators.
version : 3
Frank Person is now a member of the mailing list.
+::
>>> member = mlist.members.get_member('fperson@example.org')
>>> member
@@ -692,7 +697,7 @@ Frank Person is now a member of the mailing list.
>>> from zope.component import getUtility
>>> user_manager = getUtility(IUserManager)
- >>> user = user_manager.get_user(member.address.address)
+ >>> user = user_manager.get_user(member.address.email)
>>> print user.real_name
Frank Person
>>> print user.password
@@ -704,7 +709,9 @@ Holding unsubscription requests
Some lists, though it is rare, require moderator approval for unsubscriptions.
In this case, only the unsubscribing address is required. Like subscriptions,
-unsubscription holds can send the list's moderators an immediate notification.
+unsubscription holds can send the list's moderators an immediate
+notification.
+::
>>> mlist.admin_immed_notify = False
>>> from mailman.interfaces.member import MemberRole
@@ -776,6 +783,7 @@ subscribed.
The request can be rejected, in which case a message is sent to the member,
and the person remains a member of the mailing list.
+::
>>> moderator.handle_unsubscription(mlist, id_6, Action.reject,
... 'This list is a prison.')
diff --git a/src/mailman/model/docs/usermanager.txt b/src/mailman/model/docs/usermanager.txt
index 856221952..7b333248c 100644
--- a/src/mailman/model/docs/usermanager.txt
+++ b/src/mailman/model/docs/usermanager.txt
@@ -2,10 +2,7 @@
The user manager
================
-The IUserManager is how you create, delete, and manage users. The Mailman
-system instantiates an IUserManager for you based on the configuration
-variable MANAGERS_INIT_FUNCTION. The instance is accessible on the global
-config object.
+The ``IUserManager`` is how you create, delete, and manage users.
>>> from mailman.interfaces.usermanager import IUserManager
>>> from zope.component import getUtility
@@ -16,9 +13,10 @@ Creating users
==============
There are several ways you can create a user object. The simplest is to
-create a 'blank' user by not providing an address or real name at creation
+create a `blank` user by not providing an address or real name at creation
time. This user will have an empty string as their real name, but will not
have a password.
+::
>>> from mailman.interfaces.user import IUser
>>> from zope.interface.verify import verifyObject
@@ -26,10 +24,10 @@ have a password.
>>> verifyObject(IUser, user)
True
- >>> sorted(address.address for address in user.addresses)
- []
- >>> user.real_name
- u''
+ >>> dump_list(address.email for address in user.addresses)
+ *Empty*
+ >>> print user.real_name
+ <BLANKLINE>
>>> print user.password
None
@@ -41,52 +39,59 @@ The user has preferences, but none of them will be specified.
A user can be assigned a real name.
>>> user.real_name = 'Anne Person'
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Anne Person']
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Anne Person
A user can be assigned a password.
>>> user.password = 'secret'
- >>> sorted(user.password for user in user_manager.users)
- [u'secret']
+ >>> dump_list(user.password for user in user_manager.users)
+ secret
You can also create a user with an address to start out with.
>>> user_2 = user_manager.create_user('bperson@example.com')
>>> verifyObject(IUser, user_2)
True
- >>> sorted(address.address for address in user_2.addresses)
- [u'bperson@example.com']
- >>> sorted(user.real_name for user in user_manager.users)
- [u'', u'Anne Person']
+ >>> dump_list(address.email for address in user_2.addresses)
+ bperson@example.com
+ >>> dump_list(user.real_name for user in user_manager.users)
+ <BLANKLINE>
+ Anne Person
As above, you can assign a real name to such users.
>>> user_2.real_name = 'Ben Person'
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Anne Person', u'Ben Person']
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Anne Person
+ Ben Person
You can also create a user with just a real name.
>>> user_3 = user_manager.create_user(real_name='Claire Person')
>>> verifyObject(IUser, user_3)
True
- >>> sorted(address.address for address in user.addresses)
- []
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Anne Person', u'Ben Person', u'Claire Person']
+ >>> dump_list(address.email for address in user.addresses)
+ *Empty*
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Anne Person
+ Ben Person
+ Claire Person
Finally, you can create a user with both an address and a real name.
>>> user_4 = user_manager.create_user('dperson@example.com', 'Dan Person')
>>> verifyObject(IUser, user_3)
True
- >>> sorted(address.address for address in user_4.addresses)
- [u'dperson@example.com']
- >>> sorted(address.real_name for address in user_4.addresses)
- [u'Dan Person']
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Anne Person', u'Ben Person', u'Claire Person', u'Dan Person']
+ >>> dump_list(address.email for address in user_4.addresses)
+ dperson@example.com
+ >>> dump_list(address.real_name for address in user_4.addresses)
+ Dan Person
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Anne Person
+ Ben Person
+ Claire Person
+ Dan Person
Deleting users
@@ -96,30 +101,32 @@ You delete users by going through the user manager. The deleted user is no
longer available through the user manager iterator.
>>> user_manager.delete_user(user)
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Ben Person', u'Claire Person', u'Dan Person']
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Ben Person
+ Claire Person
+ Dan Person
Finding users
=============
-You can ask the user manager to find the IUser that controls a particular
+You can ask the user manager to find the ``IUser`` that controls a particular
email address. You'll get back the original user object if it's found. Note
-that the .get_user() method takes a string email address, not an IAddress
-object.
+that the ``.get_user()`` method takes a string email address, not an
+``IAddress`` object.
>>> address = list(user_4.addresses)[0]
- >>> found_user = user_manager.get_user(address.address)
+ >>> found_user = user_manager.get_user(address.email)
>>> found_user
<User "Dan Person" at ...>
>>> found_user is user_4
True
If the address is not in the user database or does not have a user associated
-with it, you will get None back.
+with it, you will get ``None`` back.
>>> print user_manager.get_user('zperson@example.com')
None
>>> user_4.unlink(address)
- >>> print user_manager.get_user(address.address)
+ >>> print user_manager.get_user(address.email)
None
diff --git a/src/mailman/model/docs/users.txt b/src/mailman/model/docs/users.txt
index bb0301772..bbfef8391 100644
--- a/src/mailman/model/docs/users.txt
+++ b/src/mailman/model/docs/users.txt
@@ -21,19 +21,19 @@ Users may have a real name and a password.
>>> user_1 = user_manager.create_user()
>>> user_1.password = 'my password'
>>> user_1.real_name = 'Zoe Person'
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Zoe Person']
- >>> sorted(user.password for user in user_manager.users)
- [u'my password']
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Zoe Person
+ >>> dump_list(user.password for user in user_manager.users)
+ my password
The password and real name can be changed at any time.
>>> user_1.real_name = 'Zoe X. Person'
>>> user_1.password = 'another password'
- >>> sorted(user.real_name for user in user_manager.users)
- [u'Zoe X. Person']
- >>> sorted(user.password for user in user_manager.users)
- [u'another password']
+ >>> dump_list(user.real_name for user in user_manager.users)
+ Zoe X. Person
+ >>> dump_list(user.password for user in user_manager.users)
+ another password
Users addresses
@@ -50,19 +50,25 @@ address on a user object.
<Address: Zoe Person <zperson@example.com> [not verified] at 0x...>
>>> user_1.register('zperson@example.org')
<Address: zperson@example.org [not verified] at 0x...>
- >>> sorted(address.address for address in user_1.addresses)
- [u'zperson@example.com', u'zperson@example.org']
- >>> sorted(address.real_name for address in user_1.addresses)
- [u'', u'Zoe Person']
+ >>> dump_list(address.email for address in user_1.addresses)
+ zperson@example.com
+ zperson@example.org
+ >>> dump_list(address.real_name for address in user_1.addresses)
+ <BLANKLINE>
+ Zoe Person
You can also create the address separately and then link it to the user.
>>> address_1 = user_manager.create_address('zperson@example.net')
>>> 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']
- >>> sorted(address.real_name for address in user_1.addresses)
- [u'', u'', u'Zoe Person']
+ >>> dump_list(address.email for address in user_1.addresses)
+ zperson@example.com
+ zperson@example.net
+ zperson@example.org
+ >>> dump_list(address.real_name for address in user_1.addresses)
+ <BLANKLINE>
+ <BLANKLINE>
+ Zoe Person
But don't try to link an address to more than one user.
@@ -74,7 +80,7 @@ But don't try to link an address to more than one user.
You can also ask whether a given user controls a given address.
- >>> user_1.controls(address_1.address)
+ >>> user_1.controls(address_1.email)
True
>>> user_1.controls('bperson@example.com')
False
@@ -133,7 +139,9 @@ Users have preferences, but these preferences have no default settings.
receive_own_postings : None
delivery_mode : None
-Some of these preferences are booleans and they can be set to True or False.
+Some of these preferences are booleans and they can be set to ``True`` or
+``False``.
+::
>>> from mailman.interfaces.languages import ILanguageManager
>>> getUtility(ILanguageManager).add('it', 'iso-8859-1', 'Italian')
@@ -158,10 +166,13 @@ 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']
+ >>> dump_list(address.email for address in user_1.addresses)
+ zperson@example.com
+ zperson@example.net
+ zperson@example.org
>>> com = user_manager.get_address('zperson@example.com')
>>> org = user_manager.get_address('zperson@example.org')
>>> net = user_manager.get_address('zperson@example.net')
@@ -191,18 +202,14 @@ membership role.
>>> len(members)
4
>>> def sortkey(member):
- ... return (member.address.address, member.mailing_list,
+ ... return (member.address.email, member.mailing_list,
... int(member.role))
>>> for member in sorted(members, key=sortkey):
- ... print member.address.address, member.mailing_list, member.role
+ ... print member.address.email, 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
-Cross references
-================
-
.. _`usermanager.txt`: usermanager.html
-
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index e19890cc3..89b071477 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2008-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2008-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -77,10 +77,16 @@ class Domain(Model):
@property
def url_host(self):
+ """See `IDomain`."""
# pylint: disable-msg=E1101
# no netloc member; yes it does
return urlparse(self.base_url).netloc
+ @property
+ def scheme(self):
+ """See `IDomain`."""
+ return urlparse(self.base_url).scheme
+
def confirm_url(self, token=''):
"""See `IDomain`."""
return urljoin(self.base_url, 'confirm/' + token)
diff --git a/src/mailman/model/language.py b/src/mailman/model/language.py
index c1870a1b5..57f5497f5 100644
--- a/src/mailman/model/language.py
+++ b/src/mailman/model/language.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -25,7 +25,7 @@ __all__ = [
]
-from storm.locals import *
+from storm.locals import Int, Unicode
from zope.interface import implements
from mailman.database import Model
diff --git a/src/mailman/model/listmanager.py b/src/mailman/model/listmanager.py
index e123c47f4..99c961e85 100644
--- a/src/mailman/model/listmanager.py
+++ b/src/mailman/model/listmanager.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py
index 65611f563..cdebd5ca6 100644
--- a/src/mailman/model/mailinglist.py
+++ b/src/mailman/model/mailinglist.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -61,12 +61,17 @@ class MailingList(Model):
id = Int(primary=True)
+ # XXX denotes attributes that should be part of the public interface but
+ # are currently missing.
+
# List identity
list_name = Unicode()
host_name = Unicode()
list_id = Unicode()
include_list_post_header = Bool()
include_rfc2369_headers = Bool()
+ advertised = Bool()
+ anonymous_list = Bool()
# Attributes not directly modifiable via the web u/i
created_at = DateTime()
admin_member_chunksize = Int()
@@ -77,22 +82,20 @@ class MailingList(Model):
next_digest_number = Int()
digest_last_sent_at = DateTime()
volume = Int()
- last_post_time = DateTime()
+ last_post_at = DateTime()
# Implicit destination.
acceptable_aliases_id = Int()
acceptable_alias = Reference(acceptable_aliases_id, 'AcceptableAlias.id')
# Attributes which are directly modifiable via the web u/i. The more
# complicated attributes are currently stored as pickles, though that
# will change as the schema and implementation is developed.
- accept_these_nonmembers = Pickle()
+ accept_these_nonmembers = Pickle() # XXX
admin_immed_notify = Bool()
admin_notify_mchanges = Bool()
administrivia = Bool()
- advertised = Bool()
- anonymous_list = Bool()
- archive = Bool()
- archive_private = Bool()
- archive_volume_frequency = Int()
+ archive = Bool() # XXX
+ archive_private = Bool() # XXX
+ archive_volume_frequency = Int() # XXX
# Automatic responses.
autoresponse_grace_period = TimeDelta()
autorespond_owner = Enum()
@@ -106,17 +109,18 @@ class MailingList(Model):
collapse_alternatives = Bool()
convert_html_to_plaintext = Bool()
# Bounces and bans.
- ban_list = Pickle()
- bounce_info_stale_after = TimeDelta()
- bounce_matching_headers = Unicode()
- bounce_notify_owner_on_disable = Bool()
- bounce_notify_owner_on_removal = Bool()
- bounce_processing = Bool()
- bounce_score_threshold = Int()
- bounce_unrecognized_goes_to_list_owner = Bool()
- bounce_you_are_disabled_warnings = Int()
- bounce_you_are_disabled_warnings_interval = TimeDelta()
- default_member_moderation = Bool()
+ ban_list = Pickle() # XXX
+ bounce_info_stale_after = TimeDelta() # XXX
+ bounce_matching_headers = Unicode() # XXX
+ bounce_notify_owner_on_disable = Bool() # XXX
+ bounce_notify_owner_on_removal = Bool() # XXX
+ bounce_processing = Bool() # XXX
+ bounce_score_threshold = Int() # XXX
+ bounce_unrecognized_goes_to_list_owner = Bool() # XXX
+ bounce_you_are_disabled_warnings = Int() # XXX
+ bounce_you_are_disabled_warnings_interval = TimeDelta() # XXX
+ default_member_action = Enum()
+ default_nonmember_action = Enum()
description = Unicode()
digest_footer = Unicode()
digest_header = Unicode()
@@ -141,7 +145,6 @@ class MailingList(Model):
max_days_to_hold = Int()
max_message_size = Int()
max_num_recipients = Int()
- member_moderation_action = Enum()
member_moderation_notice = Unicode()
mime_is_default_digest = Bool()
moderator_password = Unicode()
@@ -202,6 +205,7 @@ class MailingList(Model):
self.regular_members = roster.RegularMemberRoster(self)
self.digest_members = roster.DigestMemberRoster(self)
self.subscribers = roster.Subscribers(self)
+ self.nonmembers = roster.NonmemberRoster(self)
def __repr__(self):
return '<mailing list "{0}" at {1:#x}>'.format(
@@ -218,6 +222,11 @@ class MailingList(Model):
return getUtility(IDomainManager)[self.host_name]
@property
+ def scheme(self):
+ """See `IMailingList`."""
+ return self.domain.scheme
+
+ @property
def web_host(self):
"""See `IMailingList`."""
return self.domain.url_host
diff --git a/src/mailman/model/member.py b/src/mailman/model/member.py
index 34fde5f2f..5e8619324 100644
--- a/src/mailman/model/member.py
+++ b/src/mailman/model/member.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -24,14 +24,17 @@ __all__ = [
'Member',
]
-from storm.locals import *
+from storm.locals import Int, Reference, Unicode
+from zope.component import getUtility
from zope.interface import implements
from mailman.config import config
from mailman.core.constants import system_preferences
from mailman.database.model import Model
from mailman.database.types import Enum
-from mailman.interfaces.member import IMember
+from mailman.interfaces.action import Action
+from mailman.interfaces.listmanager import IListManager
+from mailman.interfaces.member import IMember, MemberRole
@@ -41,7 +44,7 @@ class Member(Model):
id = Int(primary=True)
role = Enum()
mailing_list = Unicode()
- is_moderated = Bool()
+ moderation_action = Enum()
address_id = Int()
address = Reference(address_id, 'Address.id')
@@ -52,7 +55,16 @@ class Member(Model):
self.role = role
self.mailing_list = mailing_list
self.address = address
- self.is_moderated = False
+ if role in (MemberRole.owner, MemberRole.moderator):
+ self.moderation_action = Action.accept
+ elif role is MemberRole.member:
+ self.moderation_action = getUtility(IListManager).get(
+ mailing_list).default_member_action
+ else:
+ assert role is MemberRole.nonmember, (
+ 'Invalid MemberRole: {0}'.format(role))
+ self.moderation_action = getUtility(IListManager).get(
+ mailing_list).default_nonmember_action
def __repr__(self):
return '<Member: {0} on {1} as {2}>'.format(
@@ -98,7 +110,7 @@ class Member(Model):
@property
def options_url(self):
# XXX Um, this is definitely wrong
- return 'http://example.com/' + self.address.address
+ return 'http://example.com/' + self.address.email
def unsubscribe(self):
config.db.store.remove(self.preferences)
diff --git a/src/mailman/model/message.py b/src/mailman/model/message.py
index 3e70b144d..c1e44d384 100644
--- a/src/mailman/model/message.py
+++ b/src/mailman/model/message.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -25,7 +25,7 @@ __all__ = [
'Message',
]
-from storm.locals import *
+from storm.locals import AutoReload, Int, RawStr, Unicode
from zope.interface import implements
from mailman.config import config
diff --git a/src/mailman/model/messagestore.py b/src/mailman/model/messagestore.py
index bc2323a2c..0301b2188 100644
--- a/src/mailman/model/messagestore.py
+++ b/src/mailman/model/messagestore.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
diff --git a/src/mailman/model/mime.py b/src/mailman/model/mime.py
index 3f4871a1d..8ba9adf39 100644
--- a/src/mailman/model/mime.py
+++ b/src/mailman/model/mime.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2009-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -25,7 +25,7 @@ __all__ = [
]
-from storm.locals import Bool, Int, Reference, Unicode
+from storm.locals import Int, Reference, Unicode
from zope.interface import implements
from mailman.database.model import Model
diff --git a/src/mailman/model/pending.py b/src/mailman/model/pending.py
index ae36703ce..b411e6e88 100644
--- a/src/mailman/model/pending.py
+++ b/src/mailman/model/pending.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -25,14 +25,14 @@ __all__ = [
'Pendings',
]
-import sys
+
import time
import random
import hashlib
import datetime
from lazr.config import as_timedelta
-from storm.locals import *
+from storm.locals import DateTime, Int, RawStr, ReferenceSet, Unicode
from zope.interface import implements
from zope.interface.verify import verifyObject
diff --git a/src/mailman/model/preferences.py b/src/mailman/model/preferences.py
index a5064957d..a874bc398 100644
--- a/src/mailman/model/preferences.py
+++ b/src/mailman/model/preferences.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2006-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2006-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -25,11 +25,10 @@ __all__ = [
]
-from storm.locals import *
+from storm.locals import Bool, Int, Unicode
from zope.component import getUtility
from zope.interface import implements
-from mailman.config import config
from mailman.database.model import Model
from mailman.database.types import Enum
from mailman.interfaces.languages import ILanguageManager
diff --git a/src/mailman/model/requests.py b/src/mailman/model/requests.py
index 78d077879..e1db9e63f 100644
--- a/src/mailman/model/requests.py
+++ b/src/mailman/model/requests.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -26,7 +26,7 @@ __all__ = [
from datetime import timedelta
-from storm.locals import *
+from storm.locals import AutoReload, Int, RawStr, Reference, Unicode
from zope.component import getUtility
from zope.interface import implements
diff --git a/src/mailman/model/roster.py b/src/mailman/model/roster.py
index daf964581..f3a71ee5e 100644
--- a/src/mailman/model/roster.py
+++ b/src/mailman/model/roster.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -37,7 +37,7 @@ __all__ = [
]
-from storm.expr import And, LeftJoin, Or
+from storm.expr import And, Or
from zope.interface import implements
from mailman.config import config
@@ -45,7 +45,6 @@ from mailman.interfaces.member import DeliveryMode, MemberRole
from mailman.interfaces.roster import IRoster
from mailman.model.address import Address
from mailman.model.member import Member
-from mailman.model.preferences import Preferences
@@ -100,7 +99,7 @@ class AbstractRoster:
Member,
Member.mailing_list == self._mlist.fqdn_listname,
Member.role == self.role,
- Address.address == address,
+ Address.email == address,
Member.address_id == Address.id)
if results.count() == 0:
return None
@@ -121,6 +120,14 @@ class MemberRoster(AbstractRoster):
+class NonmemberRoster(AbstractRoster):
+ """Return all the nonmembers of a list."""
+
+ name = 'nonmember'
+ role = MemberRole.nonmember
+
+
+
class OwnerRoster(AbstractRoster):
"""Return all the owners of a list."""
@@ -162,7 +169,7 @@ class AdministratorRoster(AbstractRoster):
Member.mailing_list == self._mlist.fqdn_listname,
Or(Member.role == MemberRole.moderator,
Member.role == MemberRole.owner),
- Address.address == address,
+ Address.email == address,
Member.address_id == Address.id)
if results.count() == 0:
return None
diff --git a/src/mailman/model/user.py b/src/mailman/model/user.py
index d633c5d17..f2a7c9d18 100644
--- a/src/mailman/model/user.py
+++ b/src/mailman/model/user.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -24,7 +24,7 @@ __all__ = [
'User',
]
-from storm.locals import *
+from storm.locals import Int, Reference, ReferenceSet, Unicode
from zope.interface import implements
from mailman.config import config
@@ -66,28 +66,28 @@ class User(Model):
raise AddressNotLinkedError(address)
address.user = None
- def controls(self, address):
+ def controls(self, email):
"""See `IUser`."""
- found = config.db.store.find(Address, address=address)
+ found = config.db.store.find(Address, email=email)
if found.count() == 0:
return False
assert found.count() == 1, 'Unexpected count'
return found[0].user is self
- def register(self, address, real_name=None):
+ def register(self, email, real_name=None):
"""See `IUser`."""
# First, see if the address already exists
- addrobj = config.db.store.find(Address, address=address).one()
- if addrobj is None:
+ address = config.db.store.find(Address, email=email).one()
+ if address is None:
if real_name is None:
real_name = ''
- addrobj = Address(address=address, real_name=real_name)
- addrobj.preferences = Preferences()
+ address = Address(email=email, real_name=real_name)
+ address.preferences = Preferences()
# Link the address to the user if it is not already linked.
- if addrobj.user is not None:
- raise AddressAlreadyLinkedError(addrobj)
- addrobj.user = self
- return addrobj
+ if address.user is not None:
+ raise AddressAlreadyLinkedError(address)
+ address.user = self
+ return address
@property
def memberships(self):
diff --git a/src/mailman/model/usermanager.py b/src/mailman/model/usermanager.py
index da12ba33c..067ed7795 100644
--- a/src/mailman/model/usermanager.py
+++ b/src/mailman/model/usermanager.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -39,13 +39,12 @@ from mailman.model.user import User
class UserManager:
implements(IUserManager)
- def create_user(self, address=None, real_name=None):
+ def create_user(self, email=None, real_name=None):
user = User()
user.real_name = ('' if real_name is None else real_name)
- if address:
- addrobj = Address(address, user.real_name)
- addrobj.preferences = Preferences()
- user.link(addrobj)
+ if email:
+ address = self.create_address(email, real_name)
+ user.link(address)
user.preferences = Preferences()
config.db.store.add(user)
return user
@@ -53,13 +52,8 @@ class UserManager:
def delete_user(self, user):
config.db.store.remove(user)
- @property
- def users(self):
- for user in config.db.store.find(User):
- yield user
-
- def get_user(self, address):
- addresses = config.db.store.find(Address, address=address.lower())
+ def get_user(self, email):
+ addresses = config.db.store.find(Address, email=email.lower())
if addresses.count() == 0:
return None
elif addresses.count() == 1:
@@ -67,17 +61,22 @@ class UserManager:
else:
raise AssertionError('Unexpected query count')
- def create_address(self, address, real_name=None):
- addresses = config.db.store.find(Address, address=address.lower())
+ @property
+ def users(self):
+ for user in config.db.store.find(User):
+ yield user
+
+ def create_address(self, email, real_name=None):
+ addresses = config.db.store.find(Address, email=email.lower())
if addresses.count() == 1:
found = addresses[0]
- raise ExistingAddressError(found.original_address)
+ raise ExistingAddressError(found.original_email)
assert addresses.count() == 0, 'Unexpected results'
if real_name is None:
real_name = ''
- # It's okay not to lower case the 'address' argument because the
+ # It's okay not to lower case the 'email' argument because the
# constructor will do the right thing.
- address = Address(address, real_name)
+ address = Address(email, real_name)
address.preferences = Preferences()
config.db.store.add(address)
return address
@@ -89,8 +88,8 @@ class UserManager:
address.user.unlink(address)
config.db.store.remove(address)
- def get_address(self, address):
- addresses = config.db.store.find(Address, address=address.lower())
+ def get_address(self, email):
+ addresses = config.db.store.find(Address, email=email.lower())
if addresses.count() == 0:
return None
elif addresses.count() == 1:
diff --git a/src/mailman/model/version.py b/src/mailman/model/version.py
index d56f41353..418018ac3 100644
--- a/src/mailman/model/version.py
+++ b/src/mailman/model/version.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007-2010 by the Free Software Foundation, Inc.
+# Copyright (C) 2007-2011 by the Free Software Foundation, Inc.
#
# This file is part of GNU Mailman.
#
@@ -24,7 +24,7 @@ __all__ = [
'Version',
]
-from storm.locals import *
+from storm.locals import Int, Unicode
from mailman.database.model import Model