diff options
| author | Barry Warsaw | 2007-08-05 08:42:16 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2007-08-05 08:42:16 -0400 |
| commit | e0abca9fbdde530f7517396677c87f19c86bc0c6 (patch) | |
| tree | 129da7df5fad55cb4be4d61b2f5815c0c68d1f33 | |
| parent | 2999c404a486cb0c7bd76407501e0a6b775c148a (diff) | |
| download | mailman-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.py | 2 | ||||
| -rw-r--r-- | Mailman/Errors.py | 1 | ||||
| -rw-r--r-- | Mailman/app/create.py | 16 | ||||
| -rw-r--r-- | Mailman/app/styles.py | 6 | ||||
| -rw-r--r-- | Mailman/bin/newlist.py | 20 | ||||
| -rw-r--r-- | Mailman/docs/create.txt | 123 | ||||
| -rw-r--r-- | Mailman/docs/styles.txt | 30 | ||||
| -rw-r--r-- | Mailman/interfaces/styles.py | 7 | ||||
| -rw-r--r-- | Mailman/tests/test_documentation.py | 6 |
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) |
