diff options
| author | Barry Warsaw | 2007-08-05 00:32:09 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2007-08-05 00:32:09 -0400 |
| commit | 959f34a62e0ec3cbe73da3d43640ccb6791cf3a0 (patch) | |
| tree | afcf868061fe6a5b56aeb7493c1e72e215fcce1a /Mailman/database | |
| parent | ec734fab4791c107610caf73931e570b2d1b6bd0 (diff) | |
| download | mailman-959f34a62e0ec3cbe73da3d43640ccb6791cf3a0.tar.gz mailman-959f34a62e0ec3cbe73da3d43640ccb6791cf3a0.tar.zst mailman-959f34a62e0ec3cbe73da3d43640ccb6791cf3a0.zip | |
It all started by trying to remove MailList.Create() and use the IListManager
interface to create and delete lists. Mostly that's working now, but I need
unit tests for most of the new work contained in this revision.
Implemented a rudimentary 'list styles' subsystem, along with interfaces, but
no tests yet. Moved all of MailList.InitVars() into a DefaultStyle, which is
always available at priority zero. It's used by default if there are no
matching styles for a mailing list.
Because of the list styles, we can now get rid of (almost) all InitVars()
methods. And because of /that/ we can get rid of the mixin clases whose sole
purpose was to provide an InitVars() method. Yay for code removal!
Mixin modules/classes removed: Autoresponder, GatewayManager, TopicManager.
Removed the Mailman/ext crufty extension mechanism. Extensions will now be
done using setuptools plugins. Hopefully this will take us everywhere we need
to go, but I'll add Mailman.ext back if necessary later.
Mailiman.app.create module added to implement a common, higher-level list
creation feature. This is used by bin/newlist now, though some of that
functionality (namely, ensuring the owners exist in the database, and
notifying the owners) should be moved here. The MTA plugins aren't yet
integrated into this, but need to be.
Mailman.app.plugins module added to generalize setuptools plugin management.
Defaults.DEFAULT_REPLY_GOES_TO_LIST now gets initialized with a proper enum.
Also, the duplicate DeliveryMode and DeliveryStatus enums are removed from
Defaults because they're in Mailman.constants.
Added Errors.DuplicateStyleError.
Updated Utils.list_exists() to use the new IListManager.get() interface, which
has been changed to return None if the list doesn't exist (for consistency)
instead of raising an exception. Utils.list_names() also needed to be fixed
to use config.db.list_manager.
bin/make_instance, bin/newlist, bin/rmlist changed to use parser.error()
istead of printing to sys.stderr and sys.exit(1).
bin/newlist and bin/rmlist now works with the IListManager interface, so you
can create and delete lists from the command line again. The CLI for newlist
has been much simplified; it no longer prompts for missing positional
arguments. It now uses a more traditional CLI. newlist also accepts zero to
many owners, and it ensures that the owners are all in the database. It no
longer asks for a list password, because this doesn't make sense any more.
bin/withlist has also been fixed to work with the IListManager interface.
There are lots of XXXs and FIXMEs that need to be resolved before this can
land. Also, we need to test all this stuff before it can land.
Configuration.load() is now taught to search in sys.argv[0] for
var/etc/mailman.cfg since this is where it is for egg development layouts.
Also, VAR_DIR must be abspath'd.
Added an __all__ to Mailman.constants, and added an Action enum.
The listmanager implementation has to set the mlist.created_at time. There's
also a bit of crufty refactoring going on to instantiate the roster objects
whenever the list is created or retrieved from the database.
Several MailingList column types are now set to our custom TimeDeltaType,
which knows how to store a datetime.timedelta. A SQLAlchemny converter type
is added to Mailman.database.types. I also fixed a bug in the EnumType
implementation.
Diffstat (limited to 'Mailman/database')
| -rw-r--r-- | Mailman/database/listmanager.py | 5 | ||||
| -rw-r--r-- | Mailman/database/model/mailinglist.py | 26 | ||||
| -rw-r--r-- | Mailman/database/types.py | 27 |
3 files changed, 42 insertions, 16 deletions
diff --git a/Mailman/database/listmanager.py b/Mailman/database/listmanager.py index b53bb44b3..d5a6303e6 100644 --- a/Mailman/database/listmanager.py +++ b/Mailman/database/listmanager.py @@ -18,6 +18,7 @@ """SQLAlchemy/Elixir based provider of IListManager.""" import weakref +import datetime from elixir import * from zope.interface import implements @@ -43,6 +44,7 @@ class ListManager(object): if mlist: raise Errors.MMListAlreadyExistsError(fqdn_listname) mlist = MailingList(fqdn_listname) + mlist.created_at = datetime.datetime.now() # Wrap the database model object in an application MailList object and # return the latter. Keep track of the wrapper so we can clean it up # when we're done with it. @@ -62,7 +64,8 @@ class ListManager(object): mlist = MailingList.get_by(list_name=listname, host_name=hostname) if not mlist: - raise Errors.MMUnknownListError(fqdn_listname) + return None + mlist._restore() from Mailman.MailList import MailList wrapper = self._objectmap.setdefault(mlist, MailList(mlist)) return wrapper diff --git a/Mailman/database/model/mailinglist.py b/Mailman/database/model/mailinglist.py index fce73cf25..11deb28c6 100644 --- a/Mailman/database/model/mailinglist.py +++ b/Mailman/database/model/mailinglist.py @@ -21,7 +21,7 @@ from zope.interface import implements from Mailman.Utils import fqdn_listname, split_listname from Mailman.configuration import config from Mailman.interfaces import * -from Mailman.database.types import EnumType +from Mailman.database.types import EnumType, TimeDeltaType @@ -38,6 +38,7 @@ class MailingList(Entity): has_field('list_name', Unicode), has_field('host_name', Unicode), # Attributes not directly modifiable via the web u/i + has_field('created_at', DateTime), has_field('web_page_url', Unicode), has_field('admin_member_chunksize', Integer), has_field('hold_and_cmd_autoresponses', PickleType), @@ -70,11 +71,11 @@ class MailingList(Entity): has_field('autorespond_postings', Boolean), has_field('autorespond_requests', Integer), has_field('autoresponse_admin_text', Unicode), - has_field('autoresponse_graceperiod', Integer), + has_field('autoresponse_graceperiod', TimeDeltaType), has_field('autoresponse_postings_text', Unicode), has_field('autoresponse_request_text', Unicode), has_field('ban_list', PickleType), - has_field('bounce_info_stale_after', Integer), + has_field('bounce_info_stale_after', TimeDeltaType), has_field('bounce_matching_headers', Unicode), has_field('bounce_notify_owner_on_disable', Boolean), has_field('bounce_notify_owner_on_removal', Boolean), @@ -82,7 +83,7 @@ class MailingList(Entity): has_field('bounce_score_threshold', Integer), has_field('bounce_unrecognized_goes_to_list_owner', Boolean), has_field('bounce_you_are_disabled_warnings', Integer), - has_field('bounce_you_are_disabled_warnings_interval', Integer), + has_field('bounce_you_are_disabled_warnings_interval', TimeDeltaType), has_field('collapse_alternatives', Boolean), has_field('convert_html_to_plaintext', Boolean), has_field('default_member_moderation', Boolean), @@ -152,8 +153,6 @@ class MailingList(Entity): has_field('topics', PickleType), has_field('topics_bodylines_limit', Integer), has_field('topics_enabled', Boolean), - has_field('umbrella_list', Boolean), - has_field('umbrella_member_suffix', Unicode), has_field('unsubscribe_policy', Integer), has_field('welcome_msg', Unicode), # Relationships @@ -170,8 +169,15 @@ class MailingList(Entity): self.host_name = hostname # For the pending database self.next_request_id = 1 - # Create several rosters for filtering out or querying the membership - # table. + self._restore() + # Max autoresponses per day. A mapping between addresses and a + # 2-tuple of the date of the last autoresponse and the number of + # autoresponses sent on that date. + self.hold_and_cmd_autoresponses = {} + + # XXX FIXME + def _restore(self): + # Avoid circular imports. from Mailman.database.model import roster self.owners = roster.OwnerRoster(self) self.moderators = roster.ModeratorRoster(self) @@ -179,10 +185,6 @@ class MailingList(Entity): self.members = roster.MemberRoster(self) self.regular_members = roster.RegularMemberRoster(self) self.digest_members = roster.DigestMemberRoster(self) - # Max autoresponses per day. A mapping between addresses and a - # 2-tuple of the date of the last autoresponse and the number of - # autoresponses sent on that date. - self.hold_and_cmd_autoresponses = {} @property def fqdn_listname(self): diff --git a/Mailman/database/types.py b/Mailman/database/types.py index 79ea8767d..0f0e46fa3 100644 --- a/Mailman/database/types.py +++ b/Mailman/database/types.py @@ -17,11 +17,12 @@ import sys +from datetime import timedelta from sqlalchemy import types -# SQLAlchemy custom type for storing enums in the database. +# SQLAlchemy custom type for storing munepy Enums in the database. class EnumType(types.TypeDecorator): # Enums can be stored as strings of the form: # full.path.to.Enum:intval @@ -30,7 +31,7 @@ class EnumType(types.TypeDecorator): def convert_bind_param(self, value, engine): if value is None: return None - return '%s:%s.%d' % (value.enumclass.__module__, + return '%s.%s:%d' % (value.enumclass.__module__, value.enumclass.__name__, int(value)) @@ -38,7 +39,27 @@ class EnumType(types.TypeDecorator): if value is None: return None path, intvalue = value.rsplit(':', 1) - modulename, classname = intvalue.rsplit('.', 1) + modulename, classname = path.rsplit('.', 1) __import__(modulename) cls = getattr(sys.modules[modulename], classname) return cls[int(intvalue)] + + + +class TimeDeltaType(types.TypeDecorator): + # timedeltas are stored as the string representation of three integers, + # separated by colons. The values represent the three timedelta + # attributes days, seconds, microseconds. + impl = types.String + + def convert_bind_param(self, value, engine): + if value is None: + return None + return '%s:%s:%s' % (value.days, value.seconds, value.microseconds) + + def convert_result_value(self, value, engine): + if value is None: + return None + parts = value.split(':') + assert len(parts) == 3, 'Bad timedelta representation: %s' % value + return timedelta(*(int(value) for value in parts)) |
