[prev in list] [next in list] [prev in thread] [next in thread] 

List:       mailman-cvs
Subject:    [Mailman-checkins] SF.net SVN: mailman: [8196]
From:       bwarsaw () users ! sourceforge ! net
Date:       2007-04-24 18:21:17
Message-ID: E1HgPdd-0004wN-JH () sc8-pr-svn1 ! sourceforge ! net
[Download RAW message or body]

Revision: 8196
          http://svn.sourceforge.net/mailman/?rev=8196&view=rev
Author:   bwarsaw
Date:     2007-04-24 11:21:17 -0700 (Tue, 24 Apr 2007)

Log Message:
-----------
Checkpointing.

This work, committed on a temporary experimental branch, attempts to use the
Elixir extension to SQLAlchemy to more succinctly model Mailman's database
persistency layer.  This branch also adopts zope.interfaces to define the
interfaces between the major components of Mailman.

Don't expect this branch to be functional yet, which is why it's living on a
branch (even though I think only Tokio and I were actually running anything on
the trunk ;).

Specific changes include:

- Move table definitions out of Mailman.database, into a subpackage.  For now,
  the old direct SQLAlchemy tables are placed in Mailman.database.tables, but
  I've also created Mailman.database.model for the Elixir entity subclasses,
  and that's where the major focus of this branch's work will reside.

- Added an official extension mechanism to Mailman.  The way this works is
  that you can define an EXT_DIR in your etc/mailman.cfg file and this
  directory will be woven into the Mailman.ext package.  The idea then is that
  several areas where you name a module path will be extended to take a full
  package path, which could be Mailman.ext.yourstuff.MTA for example.

- Changed the top-level Makefile's 'doinstall' target to 'justinstall' to more
  accurately reflect what that target does.

- Simplify the setup-based package installations.  Instead of trying to use
  the family of --install-* options, just give up and use --home.  This does
  require tweaking PYTHONPATH/sys.path in paths.py.in, but that's okay.  It's
  fairly easy to do that and this approach works much nicer with egg based
  packages.

- Python 2.5 is now required.  We no longer need the pysqlite package.

- Add the Elixir and zope.interface packages.

- Added Mailman/interfaces directory to hold all the official zope.interfaces
  defined interfaces for Mailman.  I'm not going to name them all here.  I'm
  sure these interface definitions will change.

- Introduce the concept of a 'list_manager', 'member_manager', and
  'message_manager'.  This gets closer to my vision of three pillars of data
  inside Mailman.  Only list_manager (i.e. IListManager) starts to approach
  usability.  All three will be singletons and will be stored on the config
  object for easy accessibility.  It is part of the vision that these be
  interchangable with alternative implementations.

  First benefit: Utils.list_names() goes away in favor of
  config.list_manager.names

- Utils.fqdn_listname() now takes an optional hostname argument.  If given,
  that is used instead of trying to split it off of the given argument.

- Mailman.database.dbcontext.DBContext starts to switch over to an Elixir
  based model instead of a raw SQLAlchemy model.  E.g. the Tables class goes
  away, as does direct manipulation of the metadata and session.

- Update some more pre-Python 2.5 idioms.

- Update LockFile to support the Python 2.5 context manager protocol.  This
  means it can now be used in with-statements.

- Allow LOG_CONFIG_FILE to be absolute or relative to the ETC_DIR.

- Added MANAGERS_INIT_FUNCTION to Defaults.py.in.  This is used to define an
  alternative initialization function that produces an IListManager,
  IUserManager, and IMessageManager.  Yes, this initialization function could
  live in Mailman.ext.yourstuff.mywizzybackend.

- Start turning some constants into enums, e.g. DeliveryMode and
  DeliveryStatus.

Modified Paths:
--------------
    branches/exp-elixir-branch/Mailman/Cgi/admin.py
    branches/exp-elixir-branch/Mailman/Cgi/listinfo.py
    branches/exp-elixir-branch/Mailman/Cgi/options.py
    branches/exp-elixir-branch/Mailman/Commands/cmd_lists.py
    branches/exp-elixir-branch/Mailman/Defaults.py.in
    branches/exp-elixir-branch/Mailman/Gui/Language.py
    branches/exp-elixir-branch/Mailman/LockFile.py
    branches/exp-elixir-branch/Mailman/MailList.py
    branches/exp-elixir-branch/Mailman/Makefile.in
    branches/exp-elixir-branch/Mailman/Queue/LMTPRunner.py
    branches/exp-elixir-branch/Mailman/Queue/MaildirRunner.py
    branches/exp-elixir-branch/Mailman/Utils.py
    branches/exp-elixir-branch/Mailman/bin/bumpdigests.py
    branches/exp-elixir-branch/Mailman/bin/change_pw.py
    branches/exp-elixir-branch/Mailman/bin/checkdbs.py
    branches/exp-elixir-branch/Mailman/bin/disabled.py
    branches/exp-elixir-branch/Mailman/bin/export.py
    branches/exp-elixir-branch/Mailman/bin/find_member.py
    branches/exp-elixir-branch/Mailman/bin/gate_news.py
    branches/exp-elixir-branch/Mailman/bin/genaliases.py
    branches/exp-elixir-branch/Mailman/bin/list_lists.py
    branches/exp-elixir-branch/Mailman/bin/list_owners.py
    branches/exp-elixir-branch/Mailman/bin/nightly_gzip.py
    branches/exp-elixir-branch/Mailman/bin/senddigests.py
    branches/exp-elixir-branch/Mailman/bin/update.py
    branches/exp-elixir-branch/Mailman/bin/withlist.py
    branches/exp-elixir-branch/Mailman/configuration.py
    branches/exp-elixir-branch/Mailman/database/Makefile.in
    branches/exp-elixir-branch/Mailman/database/__init__.py
    branches/exp-elixir-branch/Mailman/database/dbcontext.py
    branches/exp-elixir-branch/Mailman/initialize.py
    branches/exp-elixir-branch/Mailman/loginit.py
    branches/exp-elixir-branch/Makefile.in
    branches/exp-elixir-branch/configure
    branches/exp-elixir-branch/configure.in
    branches/exp-elixir-branch/misc/Makefile.in
    branches/exp-elixir-branch/misc/paths.py.in

Added Paths:
-----------
    branches/exp-elixir-branch/Mailman/database/listmanager.py
    branches/exp-elixir-branch/Mailman/database/model/
    branches/exp-elixir-branch/Mailman/database/model/Makefile.in
    branches/exp-elixir-branch/Mailman/database/model/__init__.py
    branches/exp-elixir-branch/Mailman/database/model/address.py
    branches/exp-elixir-branch/Mailman/database/model/language.py
    branches/exp-elixir-branch/Mailman/database/model/mailinglist.py
    branches/exp-elixir-branch/Mailman/database/model/roster.py
    branches/exp-elixir-branch/Mailman/database/model/version.py
    branches/exp-elixir-branch/Mailman/database/profiles.py
    branches/exp-elixir-branch/Mailman/database/rosters.py
    branches/exp-elixir-branch/Mailman/database/tables/
    branches/exp-elixir-branch/Mailman/database/tables/Makefile.in
    branches/exp-elixir-branch/Mailman/database/tables/__init__.py
    branches/exp-elixir-branch/Mailman/database/tables/addresses.py
    branches/exp-elixir-branch/Mailman/database/tables/languages.py
    branches/exp-elixir-branch/Mailman/database/tables/listdata.py
    branches/exp-elixir-branch/Mailman/database/tables/profiles.py
    branches/exp-elixir-branch/Mailman/database/tables/rosters.py
    branches/exp-elixir-branch/Mailman/database/tables/versions.py
    branches/exp-elixir-branch/Mailman/ext/
    branches/exp-elixir-branch/Mailman/ext/Makefile.in
    branches/exp-elixir-branch/Mailman/ext/__init__.py
    branches/exp-elixir-branch/Mailman/interfaces/
    branches/exp-elixir-branch/Mailman/interfaces/Makefile.in
    branches/exp-elixir-branch/Mailman/interfaces/__init__.py
    branches/exp-elixir-branch/Mailman/interfaces/address.py
    branches/exp-elixir-branch/Mailman/interfaces/deliverymode.py
    branches/exp-elixir-branch/Mailman/interfaces/deliverystatus.py
    branches/exp-elixir-branch/Mailman/interfaces/listmanager.py
    branches/exp-elixir-branch/Mailman/interfaces/member.py
    branches/exp-elixir-branch/Mailman/interfaces/mlistdigests.py
    branches/exp-elixir-branch/Mailman/interfaces/mlistemail.py
    branches/exp-elixir-branch/Mailman/interfaces/mlistid.py
    branches/exp-elixir-branch/Mailman/interfaces/mlistrequest.py
    branches/exp-elixir-branch/Mailman/interfaces/mlistrosters.py
    branches/exp-elixir-branch/Mailman/interfaces/mliststats.py
    branches/exp-elixir-branch/Mailman/interfaces/mlistweb.py
    branches/exp-elixir-branch/Mailman/interfaces/permissions.py
    branches/exp-elixir-branch/Mailman/interfaces/profile.py
    branches/exp-elixir-branch/Mailman/interfaces/roster.py
    branches/exp-elixir-branch/Mailman/interfaces/user.py
    branches/exp-elixir-branch/Mailman/interfaces/usermanager.py
    branches/exp-elixir-branch/misc/Elixir-0.3.0.tar.gz
    branches/exp-elixir-branch/misc/zope.interface-3.3.0.1.tar.gz

Removed Paths:
-------------
    branches/exp-elixir-branch/Mailman/database/address.py
    branches/exp-elixir-branch/Mailman/database/languages.py
    branches/exp-elixir-branch/Mailman/database/listdata.py
    branches/exp-elixir-branch/Mailman/database/version.py

Property Changed:
----------------
    branches/exp-elixir-branch/misc/

Modified: branches/exp-elixir-branch/Mailman/Cgi/admin.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Cgi/admin.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/Cgi/admin.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -182,10 +182,7 @@
                       bgcolor=mm_cfg.WEB_HEADER_COLOR)
     # Skip any mailing list that isn't advertised.
     advertised = []
-    listnames = list(Utils.list_names())
-    listnames.sort()
-
-    for name in listnames:
+    for name in sorted(config.list_manager.names):
         mlist = MailList.MailList(name, lock=False)
         if mlist.advertised:
             if hostname not in mlist.web_page_url:

Modified: branches/exp-elixir-branch/Mailman/Cgi/listinfo.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Cgi/listinfo.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/Cgi/listinfo.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -82,10 +82,7 @@
 
     # Skip any mailing lists that isn't advertised.
     advertised = []
-    listnames = list(Utils.list_names())
-    listnames.sort()
-
-    for name in listnames:
+    for name in sorted(config.list_manager.names):
         mlist = MailList.MailList(name, lock=False)
         if mlist.advertised:
             if hostname not in mlist.web_page_url:

Modified: branches/exp-elixir-branch/Mailman/Cgi/options.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Cgi/options.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/Cgi/options.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -895,7 +895,7 @@
 def lists_of_member(mlist, user):
     hostname = mlist.host_name
     onlists = []
-    for listname in Utils.list_names():
+    for listname in config.list_manager.names:
         # The current list will always handle things in the mainline
         if listname == mlist.internal_name():
             continue

Modified: branches/exp-elixir-branch/Mailman/Commands/cmd_lists.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Commands/cmd_lists.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/Commands/cmd_lists.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -21,8 +21,8 @@
 """
 
 from Mailman import mm_cfg
-from Mailman import Utils
 from Mailman.MailList import MailList
+from Mailman.configuration import config
 from Mailman.i18n import _
 
 
@@ -43,10 +43,8 @@
         return STOP
     hostname = mlist.host_name
     res.results.append(_('Public mailing lists at %(hostname)s:'))
-    lists = Utils.list_names()
-    lists.sort()
     i = 1
-    for listname in lists:
+    for listname in sorted(config.list_manager.names):
         if listname == mlist.internal_name():
             xlist = mlist
         else:

Modified: branches/exp-elixir-branch/Mailman/Defaults.py.in
===================================================================
--- branches/exp-elixir-branch/Mailman/Defaults.py.in	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/Defaults.py.in	2007-04-24 18:21:17 UTC (rev \
8196) @@ -26,6 +26,9 @@
 
 import os
 
+from Mailman.enum import Enum
+
+
 def seconds(s): return s
 def minutes(m): return m * 60
 def hours(h): return h * 60 * 60
@@ -79,7 +82,7 @@
 DEFAULT_HOST_NAME = None
 DEFAULT_URL = None
 
-HOME_PAGE         = 'index.html'
+HOME_PAGE = 'index.html'
 
 # Normally when a site administrator authenticates to a web page with the site
 # password, they get a cookie which authorizes them as the list admin.  It
@@ -104,6 +107,11 @@
 # Database options
 #####
 
+# Initialization function for creating the IListManager, IUserManager, and
+# IMessageManager objects, as a Python dotted name.  This function must take
+# zero arguments.
+MANAGERS_INIT_FUNCTION = 'Mailman.database.initialize'
+
 # Use this to set the SQLAlchemy database engine URL.  You generally have one
 # primary database connection for all of Mailman.  List data and most rosters
 # will store their data in this database, although external rosters may access
@@ -525,6 +533,8 @@
 # - propagate -- Boolean specifying whether to propagate log message from this
 #                logger to the root "mailman" logger.  You cannot override
 #                settings for the root logger.
+#
+# The file name may be absolute, or relative to Mailman's etc directory.
 LOG_CONFIG_FILE = None
 
 # This defines log format strings for the SMTPDirect delivery module (see
@@ -1295,6 +1305,24 @@
 Moderate = 128
 DontReceiveDuplicates = 256
 
+
+class DeliveryMode(Enum):
+    # Non-digest delivery
+    Regular = 1
+    # Digest delivery modes
+    MIME    = 2
+    Plain   = 3
+
+
+class DeliveryStatus(Enum):
+    Enabled = 0
+    # Disabled reason
+    Unknown     = 1
+    ByUser      = 2
+    ByAdmin     = 3
+    ByBounce    = 4
+
+
 # A mapping between short option tags and their flag
 OPTINFO = {'hide'    : ConcealSubscription,
            'nomail'  : DisableDelivery,

Modified: branches/exp-elixir-branch/Mailman/Gui/Language.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Gui/Language.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/Gui/Language.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -23,7 +23,7 @@
 from Mailman import i18n
 from Mailman import mm_cfg
 from Mailman.Gui.GUIBase import GUIBase
-from Mailman.database.languages import Language
+from Mailman.database.tables.languages import Language
 
 _ = i18n._
 

Modified: branches/exp-elixir-branch/Mailman/LockFile.py
===================================================================
--- branches/exp-elixir-branch/Mailman/LockFile.py	2007-04-24 17:53:43 UTC (rev 8195)
+++ branches/exp-elixir-branch/Mailman/LockFile.py	2007-04-24 18:21:17 UTC (rev 8196)
@@ -321,6 +321,16 @@
         if self._owned:
             self.finalize()
 
+    # Python 2.5 context manager protocol support.
+    def __enter__(self):
+        self.lock()
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        self.unlock()
+        # Don't suppress any exception that might have occurred.
+        return False
+
     # Use these only if you're transfering ownership to a child process across
     # a fork.  Use at your own risk, but it should be race-condition safe.
     # _transfer_to() is called in the parent, passing in the pid of the child.

Modified: branches/exp-elixir-branch/Mailman/MailList.py
===================================================================
--- branches/exp-elixir-branch/Mailman/MailList.py	2007-04-24 17:53:43 UTC (rev 8195)
+++ branches/exp-elixir-branch/Mailman/MailList.py	2007-04-24 18:21:17 UTC (rev 8196)
@@ -49,7 +49,7 @@
 from Mailman import database
 from Mailman.UserDesc import UserDesc
 from Mailman.configuration import config
-from Mailman.database.languages import Language
+from Mailman.database.tables.languages import Language
 
 # Base classes
 from Mailman import Pending
@@ -955,7 +955,7 @@
         self.setMemberName(addr, name)
         if not globally:
             return
-        for listname in Utils.list_names():
+        for listname in config.list_manager.names:
             # Don't bother with ourselves
             if listname == self.internal_name():
                 continue
@@ -1047,7 +1047,7 @@
         # oldaddr is a member.
         if not globally:
             return
-        for listname in Utils.list_names():
+        for listname in config.list_manager.names:
             # Don't bother with ourselves
             if listname == self.internal_name():
                 continue

Modified: branches/exp-elixir-branch/Mailman/Makefile.in
===================================================================
--- branches/exp-elixir-branch/Mailman/Makefile.in	2007-04-24 17:53:43 UTC (rev 8195)
+++ branches/exp-elixir-branch/Mailman/Makefile.in	2007-04-24 18:21:17 UTC (rev 8196)
@@ -44,7 +44,7 @@
 
 MODULES=	$(srcdir)/*.py
 SUBDIRS=	Cgi Archiver Handlers Bouncers Queue MTA Gui Commands \
-		bin database testing
+		bin database ext interfaces testing
 
 # Modes for directories and executables created by the install
 # process.  Default to group-writable directories but

Modified: branches/exp-elixir-branch/Mailman/Queue/LMTPRunner.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Queue/LMTPRunner.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/Queue/LMTPRunner.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -48,7 +48,6 @@
 
 from email.utils import parseaddr
 
-from Mailman import Utils
 from Mailman.Message import Message
 from Mailman.Queue.Runner import Runner
 from Mailman.Queue.sbcache import get_switchboard
@@ -122,7 +121,7 @@
             # since the set of mailing lists could have changed.  However, on
             # a big site this could be fairly expensive, so we may need to
             # cache this in some way.
-            listnames = Utils.list_names()
+            listnames = set(config.list_manager.names)
             # Parse the message data.  XXX Should we reject the message
             # immediately if it has defects?  Usually only spam has defects.
             msg = email.message_from_string(data, Message)

Modified: branches/exp-elixir-branch/Mailman/Queue/MaildirRunner.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Queue/MaildirRunner.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/Queue/MaildirRunner.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -56,7 +56,6 @@
 from email.Parser import Parser
 from email.Utils import parseaddr
 
-from Mailman import Utils
 from Mailman.Message import Message
 from Mailman.Queue.Runner import Runner
 from Mailman.Queue.sbcache import get_switchboard
@@ -101,9 +100,8 @@
         self._parser = Parser(Message)
 
     def _oneloop(self):
-        # Refresh this each time through the list.  BAW: could be too
-        # expensive.
-        listnames = Utils.list_names()
+        # Refresh this each time through the list.
+        listnames = list(config.list_manager.names)
         # Cruise through all the files currently in the new/ directory
         try:
             files = os.listdir(self._dir)

Modified: branches/exp-elixir-branch/Mailman/Utils.py
===================================================================
--- branches/exp-elixir-branch/Mailman/Utils.py	2007-04-24 17:53:43 UTC (rev 8195)
+++ branches/exp-elixir-branch/Mailman/Utils.py	2007-04-24 18:21:17 UTC (rev 8196)
@@ -40,7 +40,6 @@
 from string import ascii_letters, digits, whitespace
 
 from Mailman import Errors
-from Mailman import database
 from Mailman import passwords
 from Mailman.SafeDict import SafeDict
 from Mailman.configuration import config
@@ -66,13 +65,13 @@
 def list_exists(fqdn_listname):
     """Return true iff list `fqdn_listname' exists."""
     listname, hostname = split_listname(fqdn_listname)
-    return bool(database.find_list(listname, hostname))
+    return bool(config.list_manager.find_list(listname, hostname))
 
 
 def list_names():
     """Return the fqdn names of all lists in default list directory."""
     return ['%s@%s' % (listname, hostname)
-            for listname, hostname in database.get_list_names()]
+            for listname, hostname in config.list_manager.get_list_names()]
 
 
 def split_listname(listname):
@@ -81,8 +80,10 @@
     return listname, config.DEFAULT_EMAIL_HOST
 
 
-def fqdn_listname(listname):
-    return AT.join(split_listname(listname))
+def fqdn_listname(listname, hostname=None):
+    if hostname is None:
+        return AT.join(split_listname(listname))
+    return AT.join((listname, hostname))
 
 
 

Modified: branches/exp-elixir-branch/Mailman/bin/bumpdigests.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/bumpdigests.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/bin/bumpdigests.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -20,7 +20,6 @@
 
 from Mailman import Errors
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman.configuration import config
 from Mailman.i18n import _
@@ -52,7 +51,7 @@
     opts, args, parser = parseargs()
     config.load(opts.config)
 
-    listnames = set(args or Utils.list_names())
+    listnames = set(args or config.list_manager.names)
     if not listnames:
         print _('Nothing to do.')
         sys.exit(0)

Modified: branches/exp-elixir-branch/Mailman/bin/change_pw.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/change_pw.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/change_pw.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -114,13 +114,10 @@
 
     # Cull duplicates
     domains = set(opts.domains)
-    if opts.all:
-        listnames = set(Utils.list_names())
-    else:
-        listnames = set(opts.listnames)
+    listnames = set(config.list_manager.names if opts.all else opts.listnames)
 
     if domains:
-        for name in Utils.list_names():
+        for name in config.list_manager.names:
             mlist = openlist(name)
             if mlist.host_name in domains:
                 listnames.add(name)

Modified: branches/exp-elixir-branch/Mailman/bin/checkdbs.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/checkdbs.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/checkdbs.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -132,7 +132,7 @@
 
     i18n.set_language(config.DEFAULT_SERVER_LANGUAGE)
 
-    for name in Utils.list_names():
+    for name in config.list_manager.names:
         # The list must be locked in order to open the requests database
         mlist = MailList.MailList(name)
         try:

Modified: branches/exp-elixir-branch/Mailman/bin/disabled.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/disabled.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/disabled.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -23,7 +23,6 @@
 from Mailman import MailList
 from Mailman import MemberAdaptor
 from Mailman import Pending
-from Mailman import Utils
 from Mailman import Version
 from Mailman import loginit
 from Mailman.Bouncer import _BounceInfo
@@ -122,7 +121,7 @@
     elog = logging.getLogger('mailman.error')
     blog = logging.getLogger('mailman.bounce')
 
-    listnames = set(opts.listnames or Utils.list_names())
+    listnames = set(opts.listnames or config.list_manager.names)
     who = tuple(opts.who)
 
     msg = _('[disabled by periodic sweep and cull, no message available]')

Modified: branches/exp-elixir-branch/Mailman/bin/export.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/export.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/export.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -31,7 +31,6 @@
 from Mailman import Defaults
 from Mailman import Errors
 from Mailman import MemberAdaptor
-from Mailman import Utils
 from Mailman import Version
 from Mailman.MailList import MailList
 from Mailman.configuration import config
@@ -308,7 +307,7 @@
                     listname = '%s@%s' % (listname, config.DEFAULT_EMAIL_HOST)
                 listnames.append(listname)
         else:
-            listnames = Utils.list_names()
+            listnames = config.list_manager.names
         dumper.dump(listnames)
         dumper.close()
     finally:

Modified: branches/exp-elixir-branch/Mailman/bin/find_member.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/find_member.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/bin/find_member.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -21,7 +21,6 @@
 
 from Mailman import Errors
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman.configuration import config
 from Mailman.i18n import _
@@ -79,9 +78,8 @@
     parser, opts, args = parseargs()
     config.load(opts.config)
 
-    if not opts.listnames:
-        opts.listnames = Utils.list_names()
-    includes = set(listname.lower() for listname in opts.listnames)
+    listnames = opts.listnames or config.list_manager.names
+    includes = set(listname.lower() for listname in listnames)
     excludes = set(listname.lower() for listname in opts.excludes)
     listnames = includes - excludes
 

Modified: branches/exp-elixir-branch/Mailman/bin/gate_news.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/gate_news.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/gate_news.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -164,7 +164,7 @@
 
 
 def process_lists(glock):
-    for listname in Utils.list_names():
+    for listname in config.list_manager.names:
         glock.refresh()
         # Open the list unlocked just to check to see if it is gating news to
         # mail.  If not, we're done with the list.  Otherwise, lock the list

Modified: branches/exp-elixir-branch/Mailman/bin/genaliases.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/genaliases.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/genaliases.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -21,7 +21,6 @@
 import optparse
 
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman.configuration import config
 from Mailman.i18n import _
@@ -68,7 +67,7 @@
     lock.lock()
     # Group lists by virtual hostname
     mlists = {}
-    for listname in Utils.list_names():
+    for listname in config.list_manager.names:
         mlist = MailList.MailList(listname, lock=False)
         mlists.setdefault(mlist.host_name, []).append(mlist)
     try:

Modified: branches/exp-elixir-branch/Mailman/bin/list_lists.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/list_lists.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/list_lists.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -19,7 +19,6 @@
 
 from Mailman import Defaults
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman.i18n import _
 from Mailman.initialize import initialize
@@ -66,12 +65,10 @@
     parser, opts, args = parseargs()
     initialize(opts.config)
 
-    names = list(Utils.list_names())
-    names.sort()
     mlists = []
     longest = 0
 
-    for n in names:
+    for n in sorted(config.list_manager.names):
         mlist = MailList.MailList(n, lock=False)
         if opts.advertised and not mlist.advertised:
             continue

Modified: branches/exp-elixir-branch/Mailman/bin/list_owners.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/list_owners.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/bin/list_owners.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -18,7 +18,6 @@
 import sys
 import optparse
 
-from Mailman import Utils
 from Mailman import Version
 from Mailman.MailList import MailList
 from Mailman.configuration import config
@@ -55,7 +54,7 @@
     parser, opts, args = parseargs()
     config.load(opts.config)
 
-    listnames = args or Utils.list_names()
+    listnames = set(args or config.list_manager.names)
     bylist = {}
 
     for listname in listnames:

Modified: branches/exp-elixir-branch/Mailman/bin/nightly_gzip.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/nightly_gzip.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/bin/nightly_gzip.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -25,7 +25,6 @@
     sys.exit(0)
 
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman.configuration import config
 from Mailman.i18n import _
@@ -86,7 +85,7 @@
         return
 
     # Process all the specified lists
-    for listname in set(args or Utils.list_names()):
+    for listname in set(args or config.list_manager.names):
         mlist = MailList.MailList(listname, lock=False)
         if not mlist.archive:
             continue

Modified: branches/exp-elixir-branch/Mailman/bin/senddigests.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/senddigests.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/bin/senddigests.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -19,7 +19,6 @@
 import optparse
 
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman.i18n import _
 from Mailman.initialize import initialize
@@ -59,7 +58,7 @@
     opts, args, parser = parseargs()
     initialize(opts.config)
 
-    for listname in set(opts.listnames or Utils.list_names()):
+    for listname in set(opts.listnames or config.list_manager.names):
         mlist = MailList.MailList(listname, lock=False)
         if mlist.digest_send_periodic:
             mlist.Lock()

Modified: branches/exp-elixir-branch/Mailman/bin/update.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/update.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/update.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -607,7 +607,7 @@
     # Now we have to lock every list and re-pend all the appropriate
     # requests.  Note that this will reset all the expiration dates, but that
     # should be fine.
-    for listname in Utils.list_names():
+    for listname in config.list_manager.names:
         mlist = MailList.MailList(listname)
         # This is not the most efficient way to do this because it loads and
         # saves the pending.pck file each time. :(
@@ -682,8 +682,7 @@
                 'scripts/mailowner', 'mail/wrapper', 'Mailman/pythonlib',
                 'cgi-bin/archives', 'Mailman/MailCommandHandler'):
         remove_old_sources(mod)
-    listnames = Utils.list_names()
-    if not listnames:
+    if not config.list_manager.names:
         print _('no lists == nothing to do, exiting')
         return
     # For people with web archiving, make sure the directories
@@ -695,7 +694,7 @@
         os.path.walk("%s/public_html/archives" % config.PREFIX,
                      archive_path_fixer, "")
         print _('done')
-    for listname in listnames:
+    for listname in config.list_manager.names:
         # With 2.2.0a0, all list names grew an @domain suffix.  If you find a
         # list without that, move it now.
         if not '@' in listname:

Modified: branches/exp-elixir-branch/Mailman/bin/withlist.py
===================================================================
--- branches/exp-elixir-branch/Mailman/bin/withlist.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/bin/withlist.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -22,7 +22,6 @@
 
 from Mailman import Errors
 from Mailman import MailList
-from Mailman import Utils
 from Mailman import Version
 from Mailman import interact
 from Mailman.configuration import config
@@ -235,7 +234,8 @@
 
     r = None
     if opts.all:
-        r = [do_list(listname, args, func) for listname in Utils.list_names()]
+        r = [do_list(listname, args, func)
+             for listname in config.list_manager.names]
     elif dolist:
         listname = args.pop(0).lower().strip()
         r = do_list(listname, args, func)

Modified: branches/exp-elixir-branch/Mailman/configuration.py
===================================================================
--- branches/exp-elixir-branch/Mailman/configuration.py	2007-04-24 17:53:43 UTC (rev \
                8195)
+++ branches/exp-elixir-branch/Mailman/configuration.py	2007-04-24 18:21:17 UTC (rev \
8196) @@ -97,6 +97,7 @@
         self.DATA_DIR = datadir = os.path.join(VAR_PREFIX, 'data')
         self.ETC_DIR = etcdir   = os.path.join(VAR_PREFIX, 'etc')
         self.SPAM_DIR           = os.path.join(VAR_PREFIX, 'spam')
+        self.EXT_DIR            = os.path.join(VAR_PREFIX, 'ext')
         self.WRAPPER_DIR        = os.path.join(EXEC_PREFIX, 'mail')
         self.BIN_DIR            = os.path.join(PREFIX, 'bin')
         self.SCRIPTS_DIR        = os.path.join(PREFIX, 'scripts')

Modified: branches/exp-elixir-branch/Mailman/database/Makefile.in
===================================================================
--- branches/exp-elixir-branch/Mailman/database/Makefile.in	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/database/Makefile.in	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -1,4 +1,4 @@
-# Copyright (C) 1998-2007 by the Free Software Foundation, Inc.
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
 #
 # This program is free software; you can redistribute it and/or
 # modify it under the terms of the GNU General Public License
@@ -42,6 +42,7 @@
 SHELL=		/bin/sh
 
 MODULES=	*.py
+SUBDIRS=	tables model
 
 # Modes for directories and executables created by the install
 # process.  Default to group-writable directories but
@@ -55,17 +56,37 @@
 # Rules
 
 all:
+	for d in $(SUBDIRS); \
+	do \
+	    (cd $$d; $(MAKE)); \
+	done
 
 install: 
 	for f in $(MODULES); \
 	do \
 	    $(INSTALL) -m $(FILEMODE) $(srcdir)/$$f $(DESTDIR)$(PACKAGEDIR); \
 	done
+	for d in $(SUBDIRS); \
+	do \
+	    (cd $$d; $(MAKE) DESTDIR=$(DESTDIR) install); \
+	done
 
 finish:
+	@for d in $(SUBDIRS); \
+	do \
+	    (cd $$d; $(MAKE) DESTDIR=$(DESTDIR) finish); \
+	done
 
 clean:
+	for d in $(SUBDIRS); \
+	do \
+	    (cd $$d; $(MAKE) clean); \
+	done
 
 distclean:
 	-rm *.pyc
 	-rm Makefile
+	for d in $(SUBDIRS); \
+	do \
+	    (cd $$d; $(MAKE) distclean); \
+	done

Modified: branches/exp-elixir-branch/Mailman/database/__init__.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/__init__.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/database/__init__.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -15,31 +15,22 @@
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 # USA.
 
-# This module exposes the higher level interface methods that the rest of
-# Mailman should use.  It essentially hides the dbcontext and the SQLAlchemy
-# session from all other code.  The preferred way to use these methods is:
-#
-# from Mailman import database
-# database.add_list(foo)
+from __future__ import with_statement
 
 import os
 
+from Mailman.database.listmanager import ListManager
 
+
+
 def initialize():
-    from Mailman import database
     from Mailman.LockFile import LockFile
     from Mailman.configuration import config
-    from Mailman.database.dbcontext import dbcontext
+    from Mailman.database.dbcontext import DBContext
     # Serialize this so we don't get multiple processes trying to create the
     # database at the same time.
     lockfile = os.path.join(config.LOCK_DIR, '<dbcreatelock>')
-    lock = LockFile(lockfile)
-    lock.lock()
-    try:
+    dbcontext = DBContext()
+    with LockFile(lockfile):
         dbcontext.connect()
-    finally:
-        lock.unlock()
-    for attr in dir(dbcontext):
-        if attr.startswith('api_'):
-            exposed_name = attr[4:]
-            setattr(database, exposed_name, getattr(dbcontext, attr))
+    config.list_manager = ListManager(dbcontext)

Deleted: branches/exp-elixir-branch/Mailman/database/address.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/address.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/database/address.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -1,30 +0,0 @@
-# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-"""Email addresses."""
-
-from sqlalchemy import *
-
-
-
-def make_table(metadata, tables):
-    table = Table(
-        'Address', metadata,
-        Column('address_id',    Integer, primary_key=True),
-        Column('address',       Unicode(4096)),
-        )
-    tables.bind(table)

Modified: branches/exp-elixir-branch/Mailman/database/dbcontext.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/dbcontext.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/database/dbcontext.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -16,19 +16,16 @@
 # USA.
 
 import os
+import sys
 import logging
 import weakref
 
-from sqlalchemy import BoundMetaData, create_session
+from elixir import create_all, metadata, objectstore
 from string import Template
 from urlparse import urlparse
 
 from Mailman import Version
 from Mailman.configuration import config
-from Mailman.database import address
-from Mailman.database import languages
-from Mailman.database import listdata
-from Mailman.database import version
 from Mailman.database.txnsupport import txn
 
 
@@ -39,19 +36,9 @@
         self.fqdn_listname = mlist.fqdn_listname
 
 
-class Tables(object):
-    def bind(self, table, attrname=None):
-        if attrname is None:
-            attrname = table.name.lower()
-        setattr(self, attrname, table)
-
-
 
 class DBContext(object):
     def __init__(self):
-        self.tables = Tables()
-        self.metadata = None
-        self.session = None
         # Special transaction used only for MailList.Lock() .Save() and
         # .Unlock() interface.
         self._mlist_txns = {}
@@ -75,37 +62,29 @@
         # engines, and yes, we could have chmod'd the file after the fact, but
         # half dozen and all...
         self._touch(url)
-        self.metadata = BoundMetaData(url)
-        self.metadata.engine.echo = config.SQLALCHEMY_ECHO
-        # Create all the table objects, and then let SA conditionally create
-        # them if they don't yet exist.  NOTE: this order matters!
-        for module in (languages, address, listdata, version):
-            module.make_table(self.metadata, self.tables)
-        self.metadata.create_all()
-        # Validate schema version, updating if necessary (XXX)
-        r = self.tables.version.select(
-            self.tables.version.c.component=='schema').execute()
-        row = r.fetchone()
-        if row is None:
+        metadata.connect(url)
+        metadata.engine.echo = config.SQLALCHEMY_ECHO
+        # Load and create the Elixir active records.  This works by
+        # side-effect.
+        import Mailman.database.model
+        create_all()
+        # Validate schema version.
+        v = Mailman.database.model.Version.get_by(component='schema')
+        if not v:
             # Database has not yet been initialized
-            self.tables.version.insert().execute(
+            v = Mailman.database.model.Version(
                 component='schema',
                 version=Version.DATABASE_SCHEMA_VERSION)
-        elif row.version <> Version.DATABASE_SCHEMA_VERSION:
+            objectstore.flush()
+        elif v.version <> Version.DATABASE_SCHEMA_VERSION:
             # XXX Update schema
-            raise SchemaVersionMismatchError(row.version)
-        self.session = create_session()
+            raise SchemaVersionMismatchError(v.version)
 
-    def close(self):
-        self.session.close()
-        self.session = None
-
     def _touch(self, url):
         parts = urlparse(url)
-        # XXX Python 2.5; use parts.scheme and parts.path
-        if parts[0] <> 'sqlite':
+        if parts.scheme <> 'sqlite':
             return
-        path = os.path.normpath(parts[2])
+        path = os.path.normpath(parts.path)
         fd = os.open(path, os.O_WRONLY |  os.O_NONBLOCK | os.O_CREAT, 0666)
         # Ignore errors
         if fd > 0:
@@ -114,7 +93,7 @@
     # Cooperative method for use with @txn decorator
     def _withtxn(self, meth, *args, **kws):
         try:
-            txn = self.session.create_transaction()
+            txn = objectstore.session.current.create_transaction()
             rtn = meth(*args, **kws)
         except:
             txn.rollback()
@@ -133,7 +112,7 @@
         # Don't try to re-lock a list
         if mlist.fqdn_listname in self._mlist_txns:
             return
-        txn = self.session.create_transaction()
+        txn = objectstore.session.current.create_transaction()
         mref = MlistRef(mlist, self._unlock_mref)
         # If mlist.host_name is changed, its fqdn_listname attribute will no
         # longer match, so its transaction will not get committed when the
@@ -155,7 +134,7 @@
     def api_load(self, mlist):
         # Mark the MailList object such that future attribute accesses will
         # refresh from the database.
-        self.session.expire(mlist)
+        objectstore.session.current.expire(mlist)
 
     def api_save(self, mlist):
         # When dealing with MailLists, .Save() will always be followed by
@@ -172,29 +151,22 @@
 
     @txn
     def api_add_list(self, mlist):
-        self.session.save(mlist)
+        objectstore.session.current.save(mlist)
 
     @txn
     def api_remove_list(self, mlist):
-        self.session.delete(mlist)
+        objectstore.session.current.delete(mlist)
 
     @txn
     def api_find_list(self, listname, hostname):
         from Mailman.MailList import MailList
-        q = self.session.query(MailList)
+        q = objectstore.session.current.query(MailList)
         mlists = q.select_by(list_name=listname, host_name=hostname)
         assert len(mlists) <= 1, 'Duplicate mailing lists!'
         if mlists:
             return mlists[0]
         return None
 
-    @txn
-    def api_get_list_names(self):
-        table = self.tables.listdata
-        results = table.select().execute()
-        return [(row[table.c.list_name], row[table.c.host_name])
-                for row in results.fetchall()]
 
-
 
 dbcontext = DBContext()

Deleted: branches/exp-elixir-branch/Mailman/database/languages.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/languages.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/database/languages.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -1,53 +0,0 @@
-# Copyright (C) 2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-"""Available languages table."""
-
-from sqlalchemy import *
-
-
-
-class Language(object):
-    def __init__(self, code):
-        self.code = code
-
-    def __repr__(self):
-        return u'<Language "%s">' % self.code
-
-    def __unicode__(self):
-        return self.code
-
-    __str__ = __unicode__
-
-
-
-def make_table(metadata, tables):
-    language_table = Table(
-        'Language', metadata,
-        # Two letter language code
-        Column('language_id',   Integer, primary_key=True),
-        Column('code',          Unicode),
-        )
-    # Associate List
-    available_languages_table = Table(
-        'AvailableLanguage', metadata,
-        Column('list_id',       Integer, ForeignKey('Listdata.list_id')),
-        Column('language_id',   Integer, ForeignKey('Language.language_id')),
-        )
-    mapper(Language, language_table)
-    tables.bind(language_table)
-    tables.bind(available_languages_table, 'available_languages')

Deleted: branches/exp-elixir-branch/Mailman/database/listdata.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/listdata.py	2007-04-24 17:53:43 UTC \
                (rev 8195)
+++ branches/exp-elixir-branch/Mailman/database/listdata.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -1,192 +0,0 @@
-# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
-# USA.
-
-"""SQLAlchemy based list data storage."""
-
-from sqlalchemy import *
-
-
-
-def make_table(metadata, tables):
-    table = Table(
-        'Listdata', metadata,
-        # Attributes not directly modifiable via the web u/i
-        Column('list_id',                   Integer, primary_key=True),
-        Column('list_name',                 Unicode),
-        Column('web_page_url',              Unicode),
-        Column('admin_member_chunksize',    Integer),
-        Column('next_request_id',           Integer),
-        Column('next_digest_number',        Integer),
-        Column('admin_responses',           PickleType),
-        Column('postings_responses',        PickleType),
-        Column('request_responses',         PickleType),
-        Column('digest_last_sent_at',       Float),
-        Column('one_last_digest',           PickleType),
-        Column('volume',                    Integer),
-        Column('last_post_time',            Float),
-        # OldStyleMemberships attributes, temporarily stored as pickles.
-        Column('bounce_info',           PickleType),
-        Column('delivery_status',       PickleType),
-        Column('digest_members',        PickleType),
-        Column('language',              PickleType),
-        Column('members',               PickleType),
-        Column('passwords',             PickleType),
-        Column('topics_userinterest',   PickleType),
-        Column('user_options',          PickleType),
-        Column('usernames',             PickleType),
-        # 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.
-        Column('accept_these_nonmembers',                       PickleType),
-        Column('acceptable_aliases',                            PickleType),
-        Column('admin_immed_notify',                            Boolean),
-        Column('admin_notify_mchanges',                         Boolean),
-        Column('administrivia',                                 Boolean),
-        Column('advertised',                                    Boolean),
-        Column('anonymous_list',                                Boolean),
-        Column('archive',                                       Boolean),
-        Column('archive_private',                               Boolean),
-        Column('archive_volume_frequency',                      Integer),
-        Column('autorespond_admin',                             Boolean),
-        Column('autorespond_postings',                          Boolean),
-        Column('autorespond_requests',                          Integer),
-        Column('autoresponse_admin_text',                       Unicode),
-        Column('autoresponse_graceperiod',                      Integer),
-        Column('autoresponse_postings_text',                    Unicode),
-        Column('autoresponse_request_text',                     Unicode),
-        Column('ban_list',                                      PickleType),
-        Column('bounce_info_stale_after',                       Integer),
-        Column('bounce_matching_headers',                       Unicode),
-        Column('bounce_notify_owner_on_disable',                Boolean),
-        Column('bounce_notify_owner_on_removal',                Boolean),
-        Column('bounce_processing',                             Boolean),
-        Column('bounce_score_threshold',                        Integer),
-        Column('bounce_unrecognized_goes_to_list_owner',        Boolean),
-        Column('bounce_you_are_disabled_warnings',              Integer),
-        Column('bounce_you_are_disabled_warnings_interval',     Integer),
-        Column('collapse_alternatives',                         Boolean),
-        Column('convert_html_to_plaintext',                     Boolean),
-        Column('default_member_moderation',                     Boolean),
-        Column('description',                                   Unicode),
-        Column('digest_footer',                                 Unicode),
-        Column('digest_header',                                 Unicode),
-        Column('digest_is_default',                             Boolean),
-        Column('digest_send_periodic',                          Boolean),
-        Column('digest_size_threshhold',                        Integer),
-        Column('digest_volume_frequency',                       Integer),
-        Column('digestable',                                    Boolean),
-        Column('discard_these_nonmembers',                      PickleType),
-        Column('emergency',                                     Boolean),
-        Column('encode_ascii_prefixes',                         Boolean),
-        Column('filter_action',                                 Integer),
-        Column('filter_content',                                Boolean),
-        Column('filter_filename_extensions',                    PickleType),
-        Column('filter_mime_types',                             PickleType),
-        Column('first_strip_reply_to',                          Boolean),
-        Column('forward_auto_discards',                         Boolean),
-        Column('gateway_to_mail',                               Boolean),
-        Column('gateway_to_news',                               Boolean),
-        Column('generic_nonmember_action',                      Integer),
-        Column('goodbye_msg',                                   Unicode),
-        Column('header_filter_rules',                           PickleType),
-        Column('hold_these_nonmembers',                         PickleType),
-        Column('host_name',                                     Unicode),
-        Column('include_list_post_header',                      Boolean),
-        Column('include_rfc2369_headers',                       Boolean),
-        Column('info',                                          Unicode),
-        Column('linked_newsgroup',                              Unicode),
-        Column('max_days_to_hold',                              Integer),
-        Column('max_message_size',                              Integer),
-        Column('max_num_recipients',                            Integer),
-        Column('member_moderation_action',                      Boolean),
-        Column('member_moderation_notice',                      Unicode),
-        Column('mime_is_default_digest',                        Boolean),
-        Column('mod_password',                                  Unicode),
-        Column('moderator',                                     PickleType),
-        Column('msg_footer',                                    Unicode),
-        Column('msg_header',                                    Unicode),
-        Column('new_member_options',                            Integer),
-        Column('news_moderation',                               Boolean),
-        Column('news_prefix_subject_too',                       Boolean),
-        Column('nntp_host',                                     Unicode),
-        Column('nondigestable',                                 Boolean),
-        Column('nonmember_rejection_notice',                    Unicode),
-        Column('obscure_addresses',                             Boolean),
-        Column('owner',                                         PickleType),
-        Column('pass_filename_extensions',                      PickleType),
-        Column('pass_mime_types',                               PickleType),
-        Column('password',                                      Unicode),
-        Column('personalize',                                   Integer),
-        Column('post_id',                                       Integer),
-        Column('preferred_language',                            Unicode),
-        Column('private_roster',                                Boolean),
-        Column('real_name',                                     Unicode),
-        Column('reject_these_nonmembers',                       PickleType),
-        Column('reply_goes_to_list',                            Boolean),
-        Column('reply_to_address',                              Unicode),
-        Column('require_explicit_destination',                  Boolean),
-        Column('respond_to_post_requests',                      Boolean),
-        Column('scrub_nondigest',                               Boolean),
-        Column('send_goodbye_msg',                              Boolean),
-        Column('send_reminders',                                Boolean),
-        Column('send_welcome_msg',                              Boolean),
-        Column('subject_prefix',                                Unicode),
-        Column('subscribe_auto_approval',                       PickleType),
-        Column('subscribe_policy',                              Integer),
-        Column('topics',                                        PickleType),
-        Column('topics_bodylines_limit',                        Integer),
-        Column('topics_enabled',                                Boolean),
-        Column('umbrella_list',                                 Boolean),
-        Column('umbrella_member_suffix',                        Unicode),
-        Column('unsubscribe_policy',                            Integer),
-        Column('welcome_msg',                                   Unicode),
-        )
-    # Avoid circular imports
-    from Mailman.MailList import MailList
-    from Mailman.database.languages import Language
-    # We need to ensure MailList.InitTempVars() is called whenever a MailList
-    # instance is created from a row.  Use a mapper extension for this.
-    props = dict(available_languages=
-                 relation(Language,
-                          secondary=tables.available_languages,
-                          lazy=False))
-    mapper(MailList, table,
-           extension=MailListMapperExtension(),
-           properties=props)
-    tables.bind(table)
-
-
-
-class MailListMapperExtension(MapperExtension):
-    def populate_instance(self, mapper, context, row, mlist, ikey, isnew):
-        # Michael Bayer on the sqlalchemy mailing list:
-        #
-        # "isnew" is used to indicate that we are going to populate the
-        # instance with data from the database, *and* that this particular row
-        # is the first row in the result which has indicated the presence of
-        # this entity (i.e. the primary key points to it).  this implies that
-        # populate_instance() can be called *multiple times* for the instance,
-        # if multiple successive rows all contain its particular primary key.
-        if isnew:
-            # Get the list name and host name -- which are required by
-            # InitTempVars() from the row data.
-            list_name = row['listdata_list_name']
-            host_name = row['listdata_host_name']
-            fqdn_name = '%s@%s' % (list_name, host_name)
-            mlist.InitTempVars(fqdn_name)
-        # In all cases, let SA proceed as normal
-        return EXT_PASS

Added: branches/exp-elixir-branch/Mailman/database/listmanager.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/listmanager.py	                       \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/listmanager.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,52 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""SQLAlchemy based provider of IListManager."""
+
+from zope.interface import implements
+
+from Mailman.Utils import split_listname, fqdn_listname
+from Mailman.database.model import MailingList
+from Mailman.interfaces import IListManager
+
+
+
+class ListManager(object):
+    implements(IListManager)
+
+    def __init__(self, dbcontext):
+        self._dbcontext = dbcontext
+
+    def add(self, mlist):
+        self._dbcontext.api_add_list(mlist)
+
+    def remove(self, mlist):
+        self._dbcontext.api_remove_list(mlist)
+
+    def get(self, fqdn_listname):
+        listname, hostname = Utils.split_listname(fqdn_listname)
+        return self._dbcontext.api_find_list(listname, hostname)
+
+    @property
+    def mailing_lists(self):
+        for fqdn_listname in self.names:
+            yield self.get(fqdn_listname)
+
+    @property
+    def names(self):
+        for mlist in MailingList.select():
+            yield fqdn_listname(mlist.list_name, mlist.host_name)


Property changes on: branches/exp-elixir-branch/Mailman/database/model
___________________________________________________________________
Name: svn:ignore
   + Makefile


Added: branches/exp-elixir-branch/Mailman/database/model/Makefile.in
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/Makefile.in	                    \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/Makefile.in	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,71 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software 
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+# NOTE: Makefile.in is converted into Makefile by the configure script
+# in the parent directory.  Once configure has run, you can recreate
+# the Makefile by running just config.status.
+
+# Variables set by configure
+
+VPATH=		@srcdir@
+srcdir= 	@srcdir@
+bindir= 	@bindir@
+prefix=   	@prefix@
+exec_prefix=	@exec_prefix@
+DESTDIR=
+
+CC=		@CC@
+CHMOD=  	@CHMOD@
+INSTALL=	@INSTALL@
+
+DEFS=   	@DEFS@
+
+# Customizable but not set by configure
+
+OPT=		@OPT@
+CFLAGS=		$(OPT) $(DEFS)
+PACKAGEDIR= 	$(prefix)/Mailman/database/model
+SHELL=		/bin/sh
+
+MODULES=	*.py
+
+# Modes for directories and executables created by the install
+# process.  Default to group-writable directories but
+# user-only-writable for executables.
+DIRMODE=	775
+EXEMODE=	755
+FILEMODE=	644
+INSTALL_PROGRAM=$(INSTALL) -m $(EXEMODE)
+
+
+# Rules
+
+all:
+
+install: 
+	for f in $(MODULES); \
+	do \
+	    $(INSTALL) -m $(FILEMODE) $(srcdir)/$$f $(DESTDIR)$(PACKAGEDIR); \
+	done
+
+finish:
+
+clean:
+
+distclean:
+	-rm *.pyc
+	-rm Makefile

Added: branches/exp-elixir-branch/Mailman/database/model/__init__.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/__init__.py	                    \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/__init__.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,34 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+import os
+import sys
+
+__all__ = [
+    'Address',
+    'Language',
+    'MailingList',
+    'Roster',
+    'Version',
+    ]
+
+
+from Mailman.database.model.address import Address
+from Mailman.database.model.language import Language
+from Mailman.database.model.mailinglist import MailingList
+from Mailman.database.model.roster import Roster
+from Mailman.database.model.version import Version

Added: branches/exp-elixir-branch/Mailman/database/model/address.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/address.py	                     \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/address.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,28 @@
+# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+from elixir import *
+
+
+class Address(Entity):
+    with_fields(
+        address     = Field(Unicode),
+        verified    = Field(Boolean),
+        bounce_info = Field(PickleType),
+        )
+    # Relationships
+    has_and_belongs_to_many('rosters', of_kind='Roster')

Added: branches/exp-elixir-branch/Mailman/database/model/language.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/language.py	                    \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/language.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,24 @@
+# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+from elixir import *
+
+
+class Language(Entity):
+    with_fields(
+        code = Field(Unicode),
+        )

Added: branches/exp-elixir-branch/Mailman/database/model/mailinglist.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/mailinglist.py	                 \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/mailinglist.py	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,157 @@
+# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+from elixir import *
+
+
+class MailingList(Entity):
+    with_fields(
+        # Attributes not directly modifiable via the web u/i
+        list_name                                   = Field(Unicode),
+        web_page_url                                = Field(Unicode),
+        admin_member_chunksize                      = Field(Integer),
+        # 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.
+        next_request_id                             = Field(Integer),
+        next_digest_number                          = Field(Integer),
+        admin_responses                             = Field(PickleType),
+        postings_responses                          = Field(PickleType),
+        request_responses                           = Field(PickleType),
+        digest_last_sent_at                         = Field(Float),
+        one_last_digest                             = Field(PickleType),
+        volume                                      = Field(Integer),
+        last_post_time                              = Field(Float),
+        # OldStyleMemberships attributes, temporarily stored as pickles.
+        bounce_info                                 = Field(PickleType),
+        delivery_status                             = Field(PickleType),
+        digest_members                              = Field(PickleType),
+        language                                    = Field(PickleType),
+        members                                     = Field(PickleType),
+        passwords                                   = Field(PickleType),
+        topics_userinterest                         = Field(PickleType),
+        user_options                                = Field(PickleType),
+        usernames                                   = Field(PickleType),
+        # 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                     = Field(PickleType),
+        acceptable_aliases                          = Field(PickleType),
+        admin_immed_notify                          = Field(Boolean),
+        admin_notify_mchanges                       = Field(Boolean),
+        administrivia                               = Field(Boolean),
+        advertised                                  = Field(Boolean),
+        anonymous_list                              = Field(Boolean),
+        archive                                     = Field(Boolean),
+        archive_private                             = Field(Boolean),
+        archive_volume_frequency                    = Field(Integer),
+        autorespond_admin                           = Field(Boolean),
+        autorespond_postings                        = Field(Boolean),
+        autorespond_requests                        = Field(Integer),
+        autoresponse_admin_text                     = Field(Unicode),
+        autoresponse_graceperiod                    = Field(Integer),
+        autoresponse_postings_text                  = Field(Unicode),
+        autoresponse_request_text                   = Field(Unicode),
+        ban_list                                    = Field(PickleType),
+        bounce_info_stale_after                     = Field(Integer),
+        bounce_matching_headers                     = Field(Unicode),
+        bounce_notify_owner_on_disable              = Field(Boolean),
+        bounce_notify_owner_on_removal              = Field(Boolean),
+        bounce_processing                           = Field(Boolean),
+        bounce_score_threshold                      = Field(Integer),
+        bounce_unrecognized_goes_to_list_owner      = Field(Boolean),
+        bounce_you_are_disabled_warnings            = Field(Integer),
+        bounce_you_are_disabled_warnings_interval   = Field(Integer),
+        collapse_alternatives                       = Field(Boolean),
+        convert_html_to_plaintext                   = Field(Boolean),
+        default_member_moderation                   = Field(Boolean),
+        description                                 = Field(Unicode),
+        digest_footer                               = Field(Unicode),
+        digest_header                               = Field(Unicode),
+        digest_is_default                           = Field(Boolean),
+        digest_send_periodic                        = Field(Boolean),
+        digest_size_threshhold                      = Field(Integer),
+        digest_volume_frequency                     = Field(Integer),
+        digestable                                  = Field(Boolean),
+        discard_these_nonmembers                    = Field(PickleType),
+        emergency                                   = Field(Boolean),
+        encode_ascii_prefixes                       = Field(Boolean),
+        filter_action                               = Field(Integer),
+        filter_content                              = Field(Boolean),
+        filter_filename_extensions                  = Field(PickleType),
+        filter_mime_types                           = Field(PickleType),
+        first_strip_reply_to                        = Field(Boolean),
+        forward_auto_discards                       = Field(Boolean),
+        gateway_to_mail                             = Field(Boolean),
+        gateway_to_news                             = Field(Boolean),
+        generic_nonmember_action                    = Field(Integer),
+        goodbye_msg                                 = Field(Unicode),
+        header_filter_rules                         = Field(PickleType),
+        hold_these_nonmembers                       = Field(PickleType),
+        host_name                                   = Field(Unicode),
+        include_list_post_header                    = Field(Boolean),
+        include_rfc2369_headers                     = Field(Boolean),
+        info                                        = Field(Unicode),
+        linked_newsgroup                            = Field(Unicode),
+        max_days_to_hold                            = Field(Integer),
+        max_message_size                            = Field(Integer),
+        max_num_recipients                          = Field(Integer),
+        member_moderation_action                    = Field(Boolean),
+        member_moderation_notice                    = Field(Unicode),
+        mime_is_default_digest                      = Field(Boolean),
+        mod_password                                = Field(Unicode),
+        msg_footer                                  = Field(Unicode),
+        msg_header                                  = Field(Unicode),
+        new_member_options                          = Field(Integer),
+        news_moderation                             = Field(Boolean),
+        news_prefix_subject_too                     = Field(Boolean),
+        nntp_host                                   = Field(Unicode),
+        nondigestable                               = Field(Boolean),
+        nonmember_rejection_notice                  = Field(Unicode),
+        obscure_addresses                           = Field(Boolean),
+        pass_filename_extensions                    = Field(PickleType),
+        pass_mime_types                             = Field(PickleType),
+        password                                    = Field(Unicode),
+        personalize                                 = Field(Integer),
+        post_id                                     = Field(Integer),
+        preferred_language                          = Field(Unicode),
+        private_roster                              = Field(Boolean),
+        real_name                                   = Field(Unicode),
+        reject_these_nonmembers                     = Field(PickleType),
+        reply_goes_to_list                          = Field(Boolean),
+        reply_to_address                            = Field(Unicode),
+        require_explicit_destination                = Field(Boolean),
+        respond_to_post_requests                    = Field(Boolean),
+        scrub_nondigest                             = Field(Boolean),
+        send_goodbye_msg                            = Field(Boolean),
+        send_reminders                              = Field(Boolean),
+        send_welcome_msg                            = Field(Boolean),
+        subject_prefix                              = Field(Unicode),
+        subscribe_auto_approval                     = Field(PickleType),
+        subscribe_policy                            = Field(Integer),
+        topics                                      = Field(PickleType),
+        topics_bodylines_limit                      = Field(Integer),
+        topics_enabled                              = Field(Boolean),
+        umbrella_list                               = Field(Boolean),
+        umbrella_member_suffix                      = Field(Unicode),
+        unsubscribe_policy                          = Field(Integer),
+        welcome_msg                                 = Field(Unicode),
+        )
+    # Relationships - XXX ondelete='all, delete=orphan' ??
+    has_and_belongs_to_many('owners',               of_kind='Roster')
+    has_and_belongs_to_many('moderators',           of_kind='Roster')
+    has_and_belongs_to_many('available_languages',  of_kind='Language')

Added: branches/exp-elixir-branch/Mailman/database/model/roster.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/roster.py	                      \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/roster.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,23 @@
+# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+from elixir import *
+
+
+class Roster(Entity):
+    # Relationships
+    has_and_belongs_to_many('addresses', of_kind='Address')

Added: branches/exp-elixir-branch/Mailman/database/model/version.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/model/version.py	                     \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/model/version.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,25 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+from elixir import *
+
+
+class Version(Entity):
+    with_fields(
+        component   = Field(String),
+        version     = Field(Integer),
+        )

Added: branches/exp-elixir-branch/Mailman/database/profiles.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/profiles.py	                        \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/profiles.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -0,0 +1,77 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Mailman user profile information."""
+
+from sqlalchemy import *
+
+from Mailman import Defaults
+
+
+
+class Profile(object):
+    pass
+
+
+
+# Both of these Enum types are stored in the database as integers, and
+# converted back into their enums on retrieval.
+
+class DeliveryModeType(types.TypeDecorator):
+    impl = types.Integer
+
+    def convert_bind_param(self, value, engine):
+        return int(value)
+
+    def convert_result_value(self, value, engine):
+        return Defaults.DeliveryMode(value)
+
+
+class DeliveryStatusType(types.TypeDecorator):
+    impl = types.Integer
+
+    def convert_bind_param(self, value, engine):
+        return int(value)
+
+    def convert_result_value(self, value, engine):
+        return Defaults.DeliveryStatus(value)
+
+
+
+def make_table(metadata, tables):
+    table = Table(
+        'Profiles', metadata,
+        Column('profile_id',            Integer, primary_key=True),
+        # OldStyleMemberships attributes, temporarily stored as pickles.
+        Column('ack',                   Boolean),
+        Column('delivery_mode',         DeliveryModeType),
+        Column('delivery_status',       DeliveryStatusType),
+        Column('hide',                  Boolean),
+        Column('language',              Unicode),
+        Column('nodupes',               Boolean),
+        Column('nomail',                Boolean),
+        Column('notmetoo',              Boolean),
+        Column('password',              Unicode),
+        Column('realname',              Unicode),
+        Column('topics',                PickleType),
+        )
+    # Avoid circular references
+    from Mailman.database.address import Address
+    # profile -> address*
+    props = dict(addresses=relation(Address, cascade='all, delete-orphan'))
+    mapper(Profile, table, properties=props)
+    tables.bind(table)

Added: branches/exp-elixir-branch/Mailman/database/rosters.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/rosters.py	                        \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/rosters.py	2007-04-24 18:21:17 UTC \
(rev 8196) @@ -0,0 +1,59 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Collections of email addresses.
+
+Rosters contain email addresses.  RosterSets contain Rosters.  Most attributes
+on the listdata table take RosterSets so that it's easy to compose just about
+any combination of addresses.
+"""
+
+from sqlalchemy import *
+
+from Mailman.database.address import Address
+
+
+
+class Roster(object):
+    pass
+
+
+class RosterSet(object):
+    pass
+
+
+
+def make_table(metadata, tables):
+    table = Table(
+        'Rosters', metadata,
+        Column('roster_id', Integer, primary_key=True),
+        )
+    # roster* <-> address*
+    props = dict(addresses=
+                 relation(Address,
+                          secondary=tables.address_rosters,
+                          lazy=False))
+    mapper(Roster, table, properties=props)
+    tables.bind(table)
+    table = Table(
+        'RosterSets', metadata,
+        Column('rosterset_id',  Integer, primary_key=True),
+        )
+    # rosterset -> roster*
+    props = dict(rosters=relation(Roster, cascade='all, delete=orphan'))
+    mapper(RosterSet, table, properties=props)
+    tables.bind(table)


Property changes on: branches/exp-elixir-branch/Mailman/database/tables
___________________________________________________________________
Name: svn:ignore
   + Makefile


Added: branches/exp-elixir-branch/Mailman/database/tables/Makefile.in
===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/Makefile.in	                   \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/Makefile.in	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,71 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software 
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+# NOTE: Makefile.in is converted into Makefile by the configure script
+# in the parent directory.  Once configure has run, you can recreate
+# the Makefile by running just config.status.
+
+# Variables set by configure
+
+VPATH=		@srcdir@
+srcdir= 	@srcdir@
+bindir= 	@bindir@
+prefix=   	@prefix@
+exec_prefix=	@exec_prefix@
+DESTDIR=
+
+CC=		@CC@
+CHMOD=  	@CHMOD@
+INSTALL=	@INSTALL@
+
+DEFS=   	@DEFS@
+
+# Customizable but not set by configure
+
+OPT=		@OPT@
+CFLAGS=		$(OPT) $(DEFS)
+PACKAGEDIR= 	$(prefix)/Mailman/database/tables
+SHELL=		/bin/sh
+
+MODULES=	*.py
+
+# Modes for directories and executables created by the install
+# process.  Default to group-writable directories but
+# user-only-writable for executables.
+DIRMODE=	775
+EXEMODE=	755
+FILEMODE=	644
+INSTALL_PROGRAM=$(INSTALL) -m $(EXEMODE)
+
+
+# Rules
+
+all:
+
+install: 
+	for f in $(MODULES); \
+	do \
+	    $(INSTALL) -m $(FILEMODE) $(srcdir)/$$f $(DESTDIR)$(PACKAGEDIR); \
+	done
+
+finish:
+
+clean:
+
+distclean:
+	-rm *.pyc
+	-rm Makefile

Added: branches/exp-elixir-branch/Mailman/database/tables/__init__.py
===================================================================

Copied: branches/exp-elixir-branch/Mailman/database/tables/addresses.py (from rev \
8192, trunk/mailman/Mailman/database/address.py) \
                ===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/addresses.py	                  \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/addresses.py	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,45 @@
+# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Email addresses."""
+
+from sqlalchemy import *
+
+
+
+class Address(object):
+    pass
+
+
+def make_table(metadata, tables):
+    address_table = Table(
+        'Addresses', metadata,
+        Column('address_id',    Integer, primary_key=True),
+        Column('profile_id',    Integer, ForeignKey('Profiles.profile_id')),
+        Column('address',       Unicode),
+        Column('verified',      Boolean),
+        Column('bounce_info',   PickleType),
+        )
+    # Associate Rosters
+    address_rosters_table = Table(
+        'AddressRoster', metadata,
+        Column('roster_id',     Integer, ForeignKey('Rosters.roster_id')),
+        Column('address_id',    Integer, ForeignKey('Addresses.address_id')),
+        )
+    mapper(Address, address_table)
+    tables.bind(address_table)
+    tables.bind(address_rosters_table, 'address_rosters')

Copied: branches/exp-elixir-branch/Mailman/database/tables/languages.py (from rev \
8192, trunk/mailman/Mailman/database/languages.py) \
                ===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/languages.py	                  \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/languages.py	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,53 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Available languages table."""
+
+from sqlalchemy import *
+
+
+
+class Language(object):
+    def __init__(self, code):
+        self.code = code
+
+    def __repr__(self):
+        return u'<Language "%s">' % self.code
+
+    def __unicode__(self):
+        return self.code
+
+    __str__ = __unicode__
+
+
+
+def make_table(metadata, tables):
+    language_table = Table(
+        'Languages', metadata,
+        # Two letter language code
+        Column('language_id',   Integer, primary_key=True),
+        Column('code',          Unicode),
+        )
+    # Associate List
+    available_languages_table = Table(
+        'AvailableLanguages', metadata,
+        Column('list_id',       Integer, ForeignKey('Listdata.list_id')),
+        Column('language_id',   Integer, ForeignKey('Languages.language_id')),
+        )
+    mapper(Language, language_table)
+    tables.bind(language_table)
+    tables.bind(available_languages_table, 'available_languages')

Copied: branches/exp-elixir-branch/Mailman/database/tables/listdata.py (from rev \
8192, trunk/mailman/Mailman/database/listdata.py) \
                ===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/listdata.py	                   \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/listdata.py	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,200 @@
+# Copyright (C) 2006-2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""SQLAlchemy based list data storage."""
+
+from sqlalchemy import *
+
+
+
+def make_table(metadata, tables):
+    table = Table(
+        'Listdata', metadata,
+        # Attributes not directly modifiable via the web u/i
+        Column('list_id',                   Integer, primary_key=True),
+        Column('list_name',                 Unicode),
+        Column('web_page_url',              Unicode),
+        Column('admin_member_chunksize',    Integer),
+        # Foreign keys - XXX ondelete='all, delete=orphan' ??
+        Column('owner',     Integer, ForeignKey('RosterSets.rosterset_id')),
+        Column('moderator', Integer, ForeignKey('RosterSets.rosterset_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.
+        Column('next_request_id',           Integer),
+        Column('next_digest_number',        Integer),
+        Column('admin_responses',           PickleType),
+        Column('postings_responses',        PickleType),
+        Column('request_responses',         PickleType),
+        Column('digest_last_sent_at',       Float),
+        Column('one_last_digest',           PickleType),
+        Column('volume',                    Integer),
+        Column('last_post_time',            Float),
+        # OldStyleMemberships attributes, temporarily stored as pickles.
+        Column('bounce_info',           PickleType),
+        Column('delivery_status',       PickleType),
+        Column('digest_members',        PickleType),
+        Column('language',              PickleType),
+        Column('members',               PickleType),
+        Column('passwords',             PickleType),
+        Column('topics_userinterest',   PickleType),
+        Column('user_options',          PickleType),
+        Column('usernames',             PickleType),
+        # 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.
+        Column('accept_these_nonmembers',                       PickleType),
+        Column('acceptable_aliases',                            PickleType),
+        Column('admin_immed_notify',                            Boolean),
+        Column('admin_notify_mchanges',                         Boolean),
+        Column('administrivia',                                 Boolean),
+        Column('advertised',                                    Boolean),
+        Column('anonymous_list',                                Boolean),
+        Column('archive',                                       Boolean),
+        Column('archive_private',                               Boolean),
+        Column('archive_volume_frequency',                      Integer),
+        Column('autorespond_admin',                             Boolean),
+        Column('autorespond_postings',                          Boolean),
+        Column('autorespond_requests',                          Integer),
+        Column('autoresponse_admin_text',                       Unicode),
+        Column('autoresponse_graceperiod',                      Integer),
+        Column('autoresponse_postings_text',                    Unicode),
+        Column('autoresponse_request_text',                     Unicode),
+        Column('ban_list',                                      PickleType),
+        Column('bounce_info_stale_after',                       Integer),
+        Column('bounce_matching_headers',                       Unicode),
+        Column('bounce_notify_owner_on_disable',                Boolean),
+        Column('bounce_notify_owner_on_removal',                Boolean),
+        Column('bounce_processing',                             Boolean),
+        Column('bounce_score_threshold',                        Integer),
+        Column('bounce_unrecognized_goes_to_list_owner',        Boolean),
+        Column('bounce_you_are_disabled_warnings',              Integer),
+        Column('bounce_you_are_disabled_warnings_interval',     Integer),
+        Column('collapse_alternatives',                         Boolean),
+        Column('convert_html_to_plaintext',                     Boolean),
+        Column('default_member_moderation',                     Boolean),
+        Column('description',                                   Unicode),
+        Column('digest_footer',                                 Unicode),
+        Column('digest_header',                                 Unicode),
+        Column('digest_is_default',                             Boolean),
+        Column('digest_send_periodic',                          Boolean),
+        Column('digest_size_threshhold',                        Integer),
+        Column('digest_volume_frequency',                       Integer),
+        Column('digestable',                                    Boolean),
+        Column('discard_these_nonmembers',                      PickleType),
+        Column('emergency',                                     Boolean),
+        Column('encode_ascii_prefixes',                         Boolean),
+        Column('filter_action',                                 Integer),
+        Column('filter_content',                                Boolean),
+        Column('filter_filename_extensions',                    PickleType),
+        Column('filter_mime_types',                             PickleType),
+        Column('first_strip_reply_to',                          Boolean),
+        Column('forward_auto_discards',                         Boolean),
+        Column('gateway_to_mail',                               Boolean),
+        Column('gateway_to_news',                               Boolean),
+        Column('generic_nonmember_action',                      Integer),
+        Column('goodbye_msg',                                   Unicode),
+        Column('header_filter_rules',                           PickleType),
+        Column('hold_these_nonmembers',                         PickleType),
+        Column('host_name',                                     Unicode),
+        Column('include_list_post_header',                      Boolean),
+        Column('include_rfc2369_headers',                       Boolean),
+        Column('info',                                          Unicode),
+        Column('linked_newsgroup',                              Unicode),
+        Column('max_days_to_hold',                              Integer),
+        Column('max_message_size',                              Integer),
+        Column('max_num_recipients',                            Integer),
+        Column('member_moderation_action',                      Boolean),
+        Column('member_moderation_notice',                      Unicode),
+        Column('mime_is_default_digest',                        Boolean),
+        Column('mod_password',                                  Unicode),
+        Column('msg_footer',                                    Unicode),
+        Column('msg_header',                                    Unicode),
+        Column('new_member_options',                            Integer),
+        Column('news_moderation',                               Boolean),
+        Column('news_prefix_subject_too',                       Boolean),
+        Column('nntp_host',                                     Unicode),
+        Column('nondigestable',                                 Boolean),
+        Column('nonmember_rejection_notice',                    Unicode),
+        Column('obscure_addresses',                             Boolean),
+        Column('pass_filename_extensions',                      PickleType),
+        Column('pass_mime_types',                               PickleType),
+        Column('password',                                      Unicode),
+        Column('personalize',                                   Integer),
+        Column('post_id',                                       Integer),
+        Column('preferred_language',                            Unicode),
+        Column('private_roster',                                Boolean),
+        Column('real_name',                                     Unicode),
+        Column('reject_these_nonmembers',                       PickleType),
+        Column('reply_goes_to_list',                            Boolean),
+        Column('reply_to_address',                              Unicode),
+        Column('require_explicit_destination',                  Boolean),
+        Column('respond_to_post_requests',                      Boolean),
+        Column('scrub_nondigest',                               Boolean),
+        Column('send_goodbye_msg',                              Boolean),
+        Column('send_reminders',                                Boolean),
+        Column('send_welcome_msg',                              Boolean),
+        Column('subject_prefix',                                Unicode),
+        Column('subscribe_auto_approval',                       PickleType),
+        Column('subscribe_policy',                              Integer),
+        Column('topics',                                        PickleType),
+        Column('topics_bodylines_limit',                        Integer),
+        Column('topics_enabled',                                Boolean),
+        Column('umbrella_list',                                 Boolean),
+        Column('umbrella_member_suffix',                        Unicode),
+        Column('unsubscribe_policy',                            Integer),
+        Column('welcome_msg',                                   Unicode),
+        )
+    # Avoid circular imports
+    from Mailman.MailList import MailList
+    from Mailman.database.tables.languages import Language
+    from Mailman.database.tables.rosters import RosterSet
+    # We need to ensure MailList.InitTempVars() is called whenever a MailList
+    # instance is created from a row.  Use a mapper extension for this.
+    props = dict(
+        # listdata* <-> language*
+        available_languages= relation(Language,
+                                      secondary=tables.available_languages,
+                                      lazy=False))
+    mapper(MailList, table,
+           # The mapper extension ensures MailList.InitTempVars() is called
+           # whenever a MailList instance is created from a row.
+           extension=MailListMapperExtension(),
+           properties=props)
+    tables.bind(table)
+
+
+
+class MailListMapperExtension(MapperExtension):
+    def populate_instance(self, mapper, context, row, mlist, ikey, isnew):
+        # Michael Bayer on the sqlalchemy mailing list:
+        #
+        # "isnew" is used to indicate that we are going to populate the
+        # instance with data from the database, *and* that this particular row
+        # is the first row in the result which has indicated the presence of
+        # this entity (i.e. the primary key points to it).  this implies that
+        # populate_instance() can be called *multiple times* for the instance,
+        # if multiple successive rows all contain its particular primary key.
+        if isnew:
+            # Get the list name and host name -- which are required by
+            # InitTempVars() from the row data.
+            list_name = row['listdata_list_name']
+            host_name = row['listdata_host_name']
+            fqdn_name = '%s@%s' % (list_name, host_name)
+            mlist.InitTempVars(fqdn_name)
+        # In all cases, let SA proceed as normal
+        return EXT_PASS

Added: branches/exp-elixir-branch/Mailman/database/tables/profiles.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/profiles.py	                   \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/profiles.py	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,77 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Mailman user profile information."""
+
+from sqlalchemy import *
+
+from Mailman import Defaults
+
+
+
+class Profile(object):
+    pass
+
+
+
+# Both of these Enum types are stored in the database as integers, and
+# converted back into their enums on retrieval.
+
+class DeliveryModeType(types.TypeDecorator):
+    impl = types.Integer
+
+    def convert_bind_param(self, value, engine):
+        return int(value)
+
+    def convert_result_value(self, value, engine):
+        return Defaults.DeliveryMode(value)
+
+
+class DeliveryStatusType(types.TypeDecorator):
+    impl = types.Integer
+
+    def convert_bind_param(self, value, engine):
+        return int(value)
+
+    def convert_result_value(self, value, engine):
+        return Defaults.DeliveryStatus(value)
+
+
+
+def make_table(metadata, tables):
+    table = Table(
+        'Profiles', metadata,
+        Column('profile_id',            Integer, primary_key=True),
+        # OldStyleMemberships attributes, temporarily stored as pickles.
+        Column('ack',                   Boolean),
+        Column('delivery_mode',         DeliveryModeType),
+        Column('delivery_status',       DeliveryStatusType),
+        Column('hide',                  Boolean),
+        Column('language',              Unicode),
+        Column('nodupes',               Boolean),
+        Column('nomail',                Boolean),
+        Column('notmetoo',              Boolean),
+        Column('password',              Unicode),
+        Column('realname',              Unicode),
+        Column('topics',                PickleType),
+        )
+    # Avoid circular references
+    from Mailman.database.tables.addresses import Address
+    # profile -> address*
+    props = dict(addresses=relation(Address, cascade='all, delete-orphan'))
+    mapper(Profile, table, properties=props)
+    tables.bind(table)

Added: branches/exp-elixir-branch/Mailman/database/tables/rosters.py
===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/rosters.py	                    \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/rosters.py	2007-04-24 18:21:17 \
UTC (rev 8196) @@ -0,0 +1,59 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Collections of email addresses.
+
+Rosters contain email addresses.  RosterSets contain Rosters.  Most attributes
+on the listdata table take RosterSets so that it's easy to compose just about
+any combination of addresses.
+"""
+
+from sqlalchemy import *
+
+from Mailman.database.tables.addresses import Address
+
+
+
+class Roster(object):
+    pass
+
+
+class RosterSet(object):
+    pass
+
+
+
+def make_table(metadata, tables):
+    table = Table(
+        'Rosters', metadata,
+        Column('roster_id', Integer, primary_key=True),
+        )
+    # roster* <-> address*
+    props = dict(addresses=
+                 relation(Address,
+                          secondary=tables.address_rosters,
+                          lazy=False))
+    mapper(Roster, table, properties=props)
+    tables.bind(table)
+    table = Table(
+        'RosterSets', metadata,
+        Column('rosterset_id',  Integer, primary_key=True),
+        )
+    # rosterset -> roster*
+    props = dict(rosters=relation(Roster, cascade='all, delete=orphan'))
+    mapper(RosterSet, table, properties=props)
+    tables.bind(table)

Copied: branches/exp-elixir-branch/Mailman/database/tables/versions.py (from rev \
8192, trunk/mailman/Mailman/database/version.py) \
                ===================================================================
--- branches/exp-elixir-branch/Mailman/database/tables/versions.py	                   \
                (rev 0)
+++ branches/exp-elixir-branch/Mailman/database/tables/versions.py	2007-04-24 \
18:21:17 UTC (rev 8196) @@ -0,0 +1,31 @@

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's \
largest Open Source development site. _______________________________________________
Mailman-checkins mailing list
Mailman-checkins@python.org
Unsubscribe: http://mail.python.org/mailman/options/mailman-checkins/mailman-cvs%40progressive-comp.com



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic