summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2007-08-05 08:42:16 -0400
committerBarry Warsaw2007-08-05 08:42:16 -0400
commite0abca9fbdde530f7517396677c87f19c86bc0c6 (patch)
tree129da7df5fad55cb4be4d61b2f5815c0c68d1f33
parent2999c404a486cb0c7bd76407501e0a6b775c148a (diff)
downloadmailman-e0abca9fbdde530f7517396677c87f19c86bc0c6.tar.gz
mailman-e0abca9fbdde530f7517396677c87f19c86bc0c6.tar.zst
mailman-e0abca9fbdde530f7517396677c87f19c86bc0c6.zip
Added a doctest for the Mailman.app.create module.
BadListNameError is gone. Use InvalidEmailAddress instead. Move owner registration from bin/newlist to Mailman/app/create.py, but do not verified owner email addresses here. Eventually we'll hook in the IRegistrar stuff for unverified owner addresses. IStyleManager.register() verifies that its registering an IStyle. Added IStyleManager.unregister(), along with updated interfaces and doctests. Clean up all styles except the default one in the system documentation test harness.
-rw-r--r--Mailman/Cgi/create.py2
-rw-r--r--Mailman/Errors.py1
-rw-r--r--Mailman/app/create.py16
-rw-r--r--Mailman/app/styles.py6
-rw-r--r--Mailman/bin/newlist.py20
-rw-r--r--Mailman/docs/create.txt123
-rw-r--r--Mailman/docs/styles.txt30
-rw-r--r--Mailman/interfaces/styles.py7
-rw-r--r--Mailman/tests/test_documentation.py6
9 files changed, 188 insertions, 23 deletions
diff --git a/Mailman/Cgi/create.py b/Mailman/Cgi/create.py
index a25f240ed..f98a13aba 100644
--- a/Mailman/Cgi/create.py
+++ b/Mailman/Cgi/create.py
@@ -173,7 +173,7 @@ def process_request(doc, cgidata):
request_creation(doc, cgidata,
_('List already exists: $safelistname'))
return
- except Errors.BadListNameError, s:
+ except Errors.InvalidEmailAddress, s:
request_creation(doc, cgidata, _('Illegal list name: $s'))
return
except Errors.MMListError:
diff --git a/Mailman/Errors.py b/Mailman/Errors.py
index 594293caa..f3f7671e8 100644
--- a/Mailman/Errors.py
+++ b/Mailman/Errors.py
@@ -39,7 +39,6 @@ class MMUnknownListError(MMListError):
class MMCorruptListDatabaseError(MMListError): pass
class MMListNotReadyError(MMListError): pass
class MMListAlreadyExistsError(MMListError): pass
-class BadListNameError(MMListError): pass
# Membership exceptions
class MMMemberError(MailmanException): pass
diff --git a/Mailman/app/create.py b/Mailman/app/create.py
index 4e94a3e44..d2f85d90d 100644
--- a/Mailman/app/create.py
+++ b/Mailman/app/create.py
@@ -23,11 +23,14 @@ from Mailman.Utils import ValidateEmail
from Mailman.app.plugins import get_plugin
from Mailman.app.styles import style_manager
from Mailman.configuration import config
+from Mailman.constants import MemberRole
-def create_list(fqdn_listname):
+def create_list(fqdn_listname, owners=None):
"""Create the named list and apply styles."""
+ if owners is None:
+ owners = []
ValidateEmail(fqdn_listname)
listname, domain = Utils.split_listname(fqdn_listname)
if domain not in config.domains:
@@ -43,4 +46,15 @@ def create_list(fqdn_listname):
# XXX FIXME
## mta_plugin = get_plugin('mailman.mta')
## mta_plugin().create(mlist)
+ # Create any owners that don't yet exist, and subscribe all addresses as
+ # owners of the mailing list.
+ usermgr = config.db.user_manager
+ for owner_address in owners:
+ addr = usermgr.get_address(owner_address)
+ if addr is None:
+ # XXX Make this use an IRegistrar instead, but that requires
+ # sussing out the IDomain stuff. For now, fake it.
+ user = usermgr.create_user(owner_address)
+ addr = list(user.addresses)[0]
+ addr.subscribe(mlist, MemberRole.owner)
return mlist
diff --git a/Mailman/app/styles.py b/Mailman/app/styles.py
index e0bf58cde..606b388ed 100644
--- a/Mailman/app/styles.py
+++ b/Mailman/app/styles.py
@@ -21,6 +21,7 @@ import datetime
from operator import attrgetter
from zope.interface import implements
+from zope.interface.verify import verifyObject
from Mailman import Utils
from Mailman.Errors import DuplicateStyleError
@@ -261,10 +262,15 @@ class StyleManager:
yield style
def register(self, style):
+ verifyObject(IStyle, style)
if style.name in self._styles:
raise DuplicateStyleError(style.name)
self._styles[style.name] = style
+ def unregister(self, style):
+ # Let KeyErrors percolate up.
+ del self._styles[style.name]
+
style_manager = StyleManager()
diff --git a/Mailman/bin/newlist.py b/Mailman/bin/newlist.py
index 9692b4fd0..4396e6556 100644
--- a/Mailman/bin/newlist.py
+++ b/Mailman/bin/newlist.py
@@ -29,7 +29,6 @@ from Mailman import Version
from Mailman import i18n
from Mailman.app.create import create_list
from Mailman.configuration import config
-from Mailman.constants import MemberRole
from Mailman.initialize import initialize
_ = i18n._
@@ -104,29 +103,14 @@ def main():
# Create the mailing list, applying styles as appropriate.
try:
- mlist = create_list(fqdn_listname)
+ mlist = create_list(fqdn_listname, opts.owners)
mlist.preferred_language = opts.language
- except Errors.BadListNameError:
+ except Errors.InvalidEmailAddress:
parser.error(_('Illegal list name: $fqdn_listname'))
except Errors.MMListAlreadyExistsError:
parser.error(_('List already exists: $fqdn_listname'))
except Errors.BadDomainSpecificationError, domain:
parser.error(_('Undefined domain: $domain'))
- except Errors.MMListAlreadyExistsError:
- parser.error(_('List already exists: $fqdn_listname'))
-
- # Create any owners that don't yet exist, and subscribe all addresses as
- # owners of the mailing list.
- usermgr = config.db.user_manager
- for owner_address in opts.owners:
- addr = usermgr.get_address(owner_address)
- if addr is None:
- # XXX Make this use an IRegistrar instead, but that requires
- # sussing out the IDomain stuff. For now, fake it.
- user = usermgr.create_user(owner_address)
- addr = list(user.addresses)[0]
- addr.verified_on = datetime.datetime.now()
- addr.subscribe(mlist, MemberRole.owner)
config.db.flush()
diff --git a/Mailman/docs/create.txt b/Mailman/docs/create.txt
new file mode 100644
index 000000000..9154a5c63
--- /dev/null
+++ b/Mailman/docs/create.txt
@@ -0,0 +1,123 @@
+Application level list creation
+-------------------------------
+
+The low-level way to create a new mailing list is to use the IListManager
+interface. This interface simply adds the appropriate database entries to
+record the list's creation.
+
+There is a higher level interface for creating mailing lists which performs a
+few additional tasks such as:
+
+ * validating the list's posting address (which also serves as the list's
+ fully qualified name);
+ * ensuring that the list's domain is registered;
+ * applying all matching styles to the new list;
+ * creating and assigning list owners;
+ * notifying watchers of list creation;
+ * creating ancillary artifacts (such as the list's on-disk directory)
+
+ >>> from Mailman.app.create import create_list
+
+
+Posting address validation
+--------------------------
+
+If you try to use the higher-level interface to create a mailing list with a
+bogus posting address, you get an exception.
+
+ >>> create_list('not a valid address')
+ Traceback (most recent call last):
+ ...
+ InvalidEmailAddress: 'not a valid address'
+
+If the posting address is valid, but the domain has not been registered with
+Mailman yet, you get an exception.
+
+ >>> create_list('test@example.org')
+ Traceback (most recent call last):
+ ...
+ BadDomainSpecificationError: example.org
+
+
+Creating a list applies its styles
+----------------------------------
+
+Start by registering a test style.
+
+ >>> from zope.interface import implements
+ >>> from Mailman.interfaces import IStyle
+ >>> class TestStyle(object):
+ ... implements(IStyle)
+ ... name = 'test'
+ ... priority = 10
+ ... def apply(self, mailing_list):
+ ... # Just does something very simple.
+ ... mailing_list.msg_footer = u'test footer'
+ ... def match(self, mailing_list, styles):
+ ... # Applies to any test list
+ ... if 'test' in mailing_list.fqdn_listname:
+ ... styles.append(self)
+ >>> from Mailman.app.styles import style_manager
+ >>> style_manager.register(TestStyle())
+
+Using the higher level interface for creating a list, applies all matching
+list styles.
+
+ >>> mlist_1 = create_list('test_1@example.com')
+ >>> from Mailman.database import flush
+ >>> flush()
+ >>> mlist_1.fqdn_listname
+ 'test_1@example.com'
+ >>> mlist_1.msg_footer
+ u'test footer'
+
+
+Creating a list with owners
+---------------------------
+
+You can also specify a list of owner email addresses. If these addresses are
+not yet known, they will be registered, and new users will be linked to them.
+However the addresses are not verified.
+
+ >>> owners = ['aperson@example.com', 'bperson@example.com',
+ ... 'cperson@example.com', 'dperson@example.com']
+ >>> mlist_2 = create_list('test_2@example.com', owners)
+ >>> flush()
+ >>> mlist_2.fqdn_listname
+ 'test_2@example.com'
+ >>> mlist_2.msg_footer
+ u'test footer'
+ >>> sorted(addr.address for addr in mlist_2.owners.addresses)
+ ['aperson@example.com', 'bperson@example.com',
+ 'cperson@example.com', 'dperson@example.com']
+
+None of the owner addresses are verified.
+
+ >>> any(addr.verified_on is not None for addr in mlist_2.owners.addresses)
+ False
+
+However, all addresses are linked to users.
+
+ >>> # The owners have no names yet
+ >>> len(list(mlist_2.owners.users))
+ 4
+
+If you create a mailing list with owner addresses that are already known to
+the system, they won't be created again.
+
+ >>> from Mailman.configuration import config
+ >>> usermgr = config.db.user_manager
+ >>> user_a = usermgr.get_user('aperson@example.com')
+ >>> user_b = usermgr.get_user('bperson@example.com')
+ >>> user_c = usermgr.get_user('cperson@example.com')
+ >>> user_d = usermgr.get_user('dperson@example.com')
+ >>> user_a.real_name = 'Anne Person'
+ >>> user_b.real_name = 'Bart Person'
+ >>> user_c.real_name = 'Caty Person'
+ >>> user_d.real_name = 'Dirk Person'
+ >>> flush()
+
+ >>> mlist_3 = create_list('test_3@example.com', owners)
+ >>> flush()
+ >>> sorted(user.real_name for user in mlist_3.owners.users)
+ ['Anne Person', 'Bart Person', 'Caty Person', 'Dirk Person']
diff --git a/Mailman/docs/styles.txt b/Mailman/docs/styles.txt
index f5d9a807b..12e2744b6 100644
--- a/Mailman/docs/styles.txt
+++ b/Mailman/docs/styles.txt
@@ -64,6 +64,7 @@ New styles must implement the IStyle interface.
>>> from zope.interface import implements
>>> from Mailman.interfaces import IStyle
>>> class TestStyle(object):
+ ... implements(IStyle)
... name = 'test'
... priority = 10
... def apply(self, mailing_list):
@@ -130,13 +131,38 @@ will take effect in the new priority order.
u'test footer'
+Unregistering styles
+--------------------
+
+You can unregister a style, making it unavailable in the future.
+
+ >>> style_manager.unregister(style_2)
+ >>> sorted(style.name for style in style_manager.lookup(mlist))
+ ['test']
+
+
Corner cases
------------
If you register a style with the same name as an already registered style, you
get an exception.
- >>> style_manager.register(AnotherTestStyle())
+ >>> style_manager.register(TestStyle())
+ Traceback (most recent call last):
+ ...
+ DuplicateStyleError: test
+
+If you try to register an object that isn't a style, you get an exception.
+
+ >>> style_manager.register(object())
+ Traceback (most recent call last):
+ ...
+ DoesNotImplement: An object does not implement interface
+ <InterfaceClass Mailman.interfaces.styles.IStyle>
+
+If you try to unregister a style that isn't registered, you get an exception.
+
+ >>> style_manager.unregister(style_2)
Traceback (most recent call last):
...
- DuplicateStyleError: another
+ KeyError: 'another'
diff --git a/Mailman/interfaces/styles.py b/Mailman/interfaces/styles.py
index 7b4207bae..aa139fde8 100644
--- a/Mailman/interfaces/styles.py
+++ b/Mailman/interfaces/styles.py
@@ -84,3 +84,10 @@ class IStyleManager(Interface):
:raises DuplicateStyleError: if a style with the same name was already
registered.
"""
+
+ def unregister(style):
+ """Unregister the style.
+
+ :param style: an IStyle.
+ :raises KeyError: If the style's name is not currently registered.
+ """
diff --git a/Mailman/tests/test_documentation.py b/Mailman/tests/test_documentation.py
index bbceedcc0..0dab2c2dd 100644
--- a/Mailman/tests/test_documentation.py
+++ b/Mailman/tests/test_documentation.py
@@ -22,6 +22,8 @@ import doctest
import unittest
import Mailman
+
+from Mailman.app.styles import style_manager
from Mailman.configuration import config
from Mailman.database import flush
@@ -56,6 +58,10 @@ def cleaning_teardown(testobj):
for dirpath, dirnames, filenames in os.walk(config.QUEUE_DIR):
for filename in filenames:
os.remove(os.path.join(dirpath, filename))
+ # Remove all but the default style.
+ for style in style_manager.styles:
+ if style.name <> 'default':
+ style_manager.unregister(style)