summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mailman/app/subscriptions.py7
-rw-r--r--src/mailman/bin/tests/test_master.py2
-rw-r--r--src/mailman/database/base.py35
-rw-r--r--src/mailman/database/docs/__init__.py0
-rw-r--r--src/mailman/database/docs/migration.rst-skip207
-rw-r--r--src/mailman/database/factory.py1
-rw-r--r--src/mailman/database/model.py28
-rw-r--r--src/mailman/database/schema/__init__.py0
-rw-r--r--src/mailman/database/schema/helpers.py43
-rw-r--r--src/mailman/database/schema/mm_00000000000000_base.py35
-rw-r--r--src/mailman/database/schema/mm_20120407000000.py212
-rw-r--r--src/mailman/database/schema/mm_20121015000000.py95
-rw-r--r--src/mailman/database/schema/mm_20130406000000.py65
-rw-r--r--src/mailman/database/schema/postgres.sql349
-rw-r--r--src/mailman/database/schema/sqlite.sql327
-rw-r--r--src/mailman/database/schema/sqlite_20120407000000_01.sql280
-rw-r--r--src/mailman/database/schema/sqlite_20121015000000_01.sql230
-rw-r--r--src/mailman/database/schema/sqlite_20130406000000_01.sql46
-rw-r--r--src/mailman/database/sqlite.py4
-rw-r--r--src/mailman/database/tests/__init__.py0
-rw-r--r--src/mailman/database/tests/data/__init__.py0
-rw-r--r--src/mailman/database/tests/data/mailman_01.dbbin48128 -> 0 bytes
-rw-r--r--src/mailman/database/tests/data/migration_postgres_1.sql133
-rw-r--r--src/mailman/database/tests/data/migration_sqlite_1.sql133
-rw-r--r--src/mailman/database/tests/test_migrations.py513
-rw-r--r--src/mailman/database/types.py45
-rw-r--r--src/mailman/interfaces/database.py2
-rw-r--r--src/mailman/interfaces/messages.py2
-rw-r--r--src/mailman/model/address.py9
-rw-r--r--src/mailman/model/autorespond.py19
-rw-r--r--src/mailman/model/bans.py4
-rw-r--r--src/mailman/model/bounce.py6
-rw-r--r--src/mailman/model/digests.py2
-rw-r--r--src/mailman/model/docs/messagestore.rst5
-rw-r--r--src/mailman/model/domain.py2
-rw-r--r--src/mailman/model/language.py2
-rw-r--r--src/mailman/model/mailinglist.py157
-rw-r--r--src/mailman/model/member.py6
-rw-r--r--src/mailman/model/message.py3
-rw-r--r--src/mailman/model/messagestore.py26
-rw-r--r--src/mailman/model/mime.py4
-rw-r--r--src/mailman/model/pending.py35
-rw-r--r--src/mailman/model/preferences.py6
-rw-r--r--src/mailman/model/requests.py11
-rw-r--r--src/mailman/model/roster.py4
-rw-r--r--src/mailman/model/tests/test_listmanager.py7
-rw-r--r--src/mailman/model/tests/test_requests.py4
-rw-r--r--src/mailman/model/uid.py3
-rw-r--r--src/mailman/model/user.py27
-rw-r--r--src/mailman/model/version.py47
-rw-r--r--src/mailman/rest/validator.py2
-rw-r--r--src/mailman/utilities/importer.py15
52 files changed, 245 insertions, 2955 deletions
diff --git a/src/mailman/app/subscriptions.py b/src/mailman/app/subscriptions.py
index 303303e70..99c6ab2de 100644
--- a/src/mailman/app/subscriptions.py
+++ b/src/mailman/app/subscriptions.py
@@ -88,8 +88,7 @@ class SubscriptionService:
@dbconnection
def get_member(self, store, member_id):
"""See `ISubscriptionService`."""
- members = store.query(Member).filter(
- Member._member_id == member_id)
+ members = store.query(Member).filter(Member._member_id == member_id)
if members.count() == 0:
return None
else:
@@ -117,7 +116,7 @@ class SubscriptionService:
if address is None or user is None:
return []
query.append(or_(Member.address_id == address.id,
- Member.user_id == user.id))
+ Member.user_id == user.id))
else:
# subscriber is a user id.
user = user_manager.get_user_by_id(subscriber)
@@ -126,7 +125,7 @@ class SubscriptionService:
if len(address_ids) == 0 or user is None:
return []
query.append(or_(Member.user_id == user.id,
- Member.address_id.in_(address_ids)))
+ Member.address_id.in_(address_ids)))
# Calculate the rest of the query expression, which will get And'd
# with the Or clause above (if there is one).
if list_id is not None:
diff --git a/src/mailman/bin/tests/test_master.py b/src/mailman/bin/tests/test_master.py
index 924fbeafd..269edaa23 100644
--- a/src/mailman/bin/tests/test_master.py
+++ b/src/mailman/bin/tests/test_master.py
@@ -55,7 +55,7 @@ class TestMasterLock(unittest.TestCase):
lock = master.acquire_lock_1(False, self.lock_file)
is_locked = lock.is_locked
lock.unlock()
- self.failUnless(is_locked)
+ self.assertTrue(is_locked)
def test_master_state(self):
my_lock = Lock(self.lock_file)
diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py
index f379b3124..c4b04b329 100644
--- a/src/mailman/database/base.py
+++ b/src/mailman/database/base.py
@@ -19,28 +19,22 @@ from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
- 'StormBaseDatabase',
+ 'SABaseDatabase',
]
-import os
-import sys
import logging
-from lazr.config import as_boolean
-from pkg_resources import resource_listdir, resource_string
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
-from sqlalchemy.orm.session import Session
from zope.interface import implementer
from mailman.config import config
from mailman.interfaces.database import IDatabase
-from mailman.model.version import Version
from mailman.utilities.string import expand
-log = logging.getLogger('mailman.config')
+log = logging.getLogger('mailman.config')
NL = '\n'
@@ -53,17 +47,15 @@ class SABaseDatabase:
"""
# Tag used to distinguish the database being used. Override this in base
# classes.
-
TAG = ''
def __init__(self):
self.url = None
self.store = None
- self.transaction = None
def begin(self):
"""See `IDatabase`."""
- # SA does this for us.
+ # SQLAlchemy does this for us.
pass
def commit(self):
@@ -102,10 +94,24 @@ class SABaseDatabase:
"""
pass
+ def _prepare(self, url):
+ """Prepare the database for creation.
+
+ Some database backends need to do so me prep work before letting Storm
+ create the database. For example, we have to touch the SQLite .db
+ file first so that it has the proper file modes.
+ """
+ pass
+
+ # XXX Abhilash removed teh _prepare() method. Is that because SA takes
+ # care of this for us? If so, then the comment below must be updated.
+ # For reference, the SQLite bug is marked "won't fix".
+
def initialize(self, debug=None):
- """See `IDatabase`"""
- # Calculate the engine url
+ """See `IDatabase`."""
+ # Calculate the engine url.
url = expand(config.database.url, config.paths)
+ self._prepare(url)
log.debug('Database url: %s', url)
# XXX By design of SQLite, database file creation does not honor
# umask. See their ticket #1193:
@@ -127,6 +133,9 @@ class SABaseDatabase:
self.store = session()
self.store.commit()
+ # XXX We should probably rename load_migrations() and perhaps get rid of
+ # load_sql(). The latter is never called any more.
+
def load_migrations(self, until=None):
"""Load schema migrations.
diff --git a/src/mailman/database/docs/__init__.py b/src/mailman/database/docs/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/src/mailman/database/docs/__init__.py
+++ /dev/null
diff --git a/src/mailman/database/docs/migration.rst-skip b/src/mailman/database/docs/migration.rst-skip
deleted file mode 100644
index fafdfaf26..000000000
--- a/src/mailman/database/docs/migration.rst-skip
+++ /dev/null
@@ -1,207 +0,0 @@
-=================
-Schema migrations
-=================
-
-The SQL database schema will over time require upgrading to support new
-features. This is supported via schema migration.
-
-Migrations are embodied in individual Python classes, which themselves may
-load SQL into the database. The naming scheme for migration files is:
-
- mm_YYYYMMDDHHMMSS_comment.py
-
-where `YYYYMMDDHHMMSS` is a required numeric year, month, day, hour, minute,
-and second specifier providing unique ordering for processing. Only this
-component of the file name is used to determine the ordering. The prefix is
-required due to Python module naming requirements, but it is actually
-ignored. `mm_` is reserved for Mailman's own use.
-
-The optional `comment` part of the file name can be used as a short
-description for the migration, although comments and docstrings in the
-migration files should be used for more detailed descriptions.
-
-Migrations are applied automatically when Mailman starts up, but can also be
-applied at any time by calling in the API directly. Once applied, a
-migration's version string is registered so it will not be applied again.
-
-We see that the base migration, as well as subsequent standard migrations, are
-already applied.
-
- >>> from mailman.model.version import Version
- >>> results = config.db.store.find(Version, component='schema')
- >>> results.count()
- 4
- >>> versions = sorted(result.version for result in results)
- >>> for version in versions:
- ... print(version)
- 00000000000000
- 20120407000000
- 20121015000000
- 20130406000000
-
-
-Migrations
-==========
-
-Migrations can be loaded at any time, and can be found in the migrations path
-specified in the configuration file.
-
-.. Create a temporary directory for the migrations::
-
- >>> import os, sys, tempfile
- >>> tempdir = tempfile.mkdtemp()
- >>> path = os.path.join(tempdir, 'migrations')
- >>> os.makedirs(path)
- >>> sys.path.append(tempdir)
- >>> config.push('migrations', """
- ... [database]
- ... migrations_path: migrations
- ... """)
-
-.. Clean this up at the end of the doctest.
- >>> def cleanup():
- ... import shutil
- ... from mailman.config import config
- ... config.pop('migrations')
- ... shutil.rmtree(tempdir)
- >>> cleanups.append(cleanup)
-
-Here is an example migrations module. The key part of this interface is the
-``upgrade()`` method, which takes four arguments:
-
- * `database` - The database class, as derived from `StormBaseDatabase`
- * `store` - The Storm `Store` object.
- * `version` - The version string as derived from the migrations module's file
- name. This will include only the `YYYYMMDDHHMMSS` string.
- * `module_path` - The dotted module path to the migrations module, suitable
- for lookup in `sys.modules`.
-
-This migration module just adds a marker to the `version` table.
-
- >>> with open(os.path.join(path, '__init__.py'), 'w') as fp:
- ... pass
- >>> with open(os.path.join(path, 'mm_20159999000000.py'), 'w') as fp:
- ... print("""
- ... from __future__ import unicode_literals
- ... from mailman.model.version import Version
- ... def upgrade(database, store, version, module_path):
- ... v = Version(component='test', version=version)
- ... store.add(v)
- ... database.load_schema(store, version, None, module_path)
- ... """, file=fp)
-
-This will load the new migration, since it hasn't been loaded before.
-
- >>> config.db.load_migrations()
- >>> results = config.db.store.find(Version, component='schema')
- >>> for result in sorted(result.version for result in results):
- ... print(result)
- 00000000000000
- 20120407000000
- 20121015000000
- 20130406000000
- 20159999000000
- >>> test = config.db.store.find(Version, component='test').one()
- >>> print(test.version)
- 20159999000000
-
-Migrations will only be loaded once.
-
- >>> with open(os.path.join(path, 'mm_20159999000001.py'), 'w') as fp:
- ... print("""
- ... from __future__ import unicode_literals
- ... from mailman.model.version import Version
- ... _marker = 801
- ... def upgrade(database, store, version, module_path):
- ... global _marker
- ... # Pad enough zeros on the left to reach 14 characters wide.
- ... marker = '{0:=#014d}'.format(_marker)
- ... _marker += 1
- ... v = Version(component='test', version=marker)
- ... store.add(v)
- ... database.load_schema(store, version, None, module_path)
- ... """, file=fp)
-
-The first time we load this new migration, we'll get the 801 marker.
-
- >>> config.db.load_migrations()
- >>> results = config.db.store.find(Version, component='schema')
- >>> for result in sorted(result.version for result in results):
- ... print(result)
- 00000000000000
- 20120407000000
- 20121015000000
- 20130406000000
- 20159999000000
- 20159999000001
- >>> test = config.db.store.find(Version, component='test')
- >>> for marker in sorted(marker.version for marker in test):
- ... print(marker)
- 00000000000801
- 20159999000000
-
-We do not get an 802 marker because the migration has already been loaded.
-
- >>> config.db.load_migrations()
- >>> results = config.db.store.find(Version, component='schema')
- >>> for result in sorted(result.version for result in results):
- ... print(result)
- 00000000000000
- 20120407000000
- 20121015000000
- 20130406000000
- 20159999000000
- 20159999000001
- >>> test = config.db.store.find(Version, component='test')
- >>> for marker in sorted(marker.version for marker in test):
- ... print(marker)
- 00000000000801
- 20159999000000
-
-
-Partial upgrades
-================
-
-It's possible (mostly for testing purposes) to only do a partial upgrade, by
-providing a timestamp to `load_migrations()`. To demonstrate this, we add two
-additional migrations, intended to be applied in sequential order.
-
- >>> from shutil import copyfile
- >>> from mailman.testing.helpers import chdir
- >>> with chdir(path):
- ... copyfile('mm_20159999000000.py', 'mm_20159999000002.py')
- ... copyfile('mm_20159999000000.py', 'mm_20159999000003.py')
- ... copyfile('mm_20159999000000.py', 'mm_20159999000004.py')
-
-Now, only migrate to the ...03 timestamp.
-
- >>> config.db.load_migrations('20159999000003')
-
-You'll notice that the ...04 version is not present.
-
- >>> results = config.db.store.find(Version, component='schema')
- >>> for result in sorted(result.version for result in results):
- ... print(result)
- 00000000000000
- 20120407000000
- 20121015000000
- 20130406000000
- 20159999000000
- 20159999000001
- 20159999000002
- 20159999000003
-
-
-.. cleanup:
- Because the Version table holds schema migration data, it will not be
- cleaned up by the standard test suite. This is generally not a problem
- for SQLite since each test gets a new database file, but for PostgreSQL,
- this will cause migration.rst to fail on subsequent runs. So let's just
- clean up the database explicitly.
-
- >>> if config.db.TAG != 'sqlite':
- ... results = config.db.store.execute("""
- ... DELETE FROM version WHERE version.version >= '201299990000'
- ... OR version.component = 'test';
- ... """)
- ... config.db.commit()
diff --git a/src/mailman/database/factory.py b/src/mailman/database/factory.py
index 64fcc242c..450672e5b 100644
--- a/src/mailman/database/factory.py
+++ b/src/mailman/database/factory.py
@@ -62,6 +62,7 @@ class DatabaseFactory:
def _reset(self):
"""See `IDatabase`."""
+ # Avoid a circular import at module level.
from mailman.database.model import Model
self.store.rollback()
self._pre_reset(self.store)
diff --git a/src/mailman/database/model.py b/src/mailman/database/model.py
index d86ebb80e..f8a15162c 100644
--- a/src/mailman/database/model.py
+++ b/src/mailman/database/model.py
@@ -26,23 +26,31 @@ __all__ = [
import contextlib
-from operator import attrgetter
from sqlalchemy.ext.declarative import declarative_base
from mailman.config import config
-class ModelMeta(object):
- """Do more magic on table classes."""
+class ModelMeta:
+ """The custom metaclass for all model base classes.
+
+ This is used in the test suite to quickly reset the database after each
+ test. It works by iterating over all the tables, deleting each. The test
+ suite will then recreate the tables before each test.
+ """
@staticmethod
def _reset(db):
- meta = Model.metadata
- engine = config.db.engine
- with contextlib.closing(engine.connect()) as con:
- trans = con.begin()
- for table in reversed(meta.sorted_tables):
- con.execute(table.delete())
- trans.commit()
+ with contextlib.closing(config.db.engine.connect()) as connection:
+ transaction = connection.begin()
+ try:
+ for table in reversed(Model.metadata.sorted_tables):
+ connection.execute(table.delete())
+ except:
+ transaction.abort()
+ raise
+ else:
+ transaction.commit()
+
Model = declarative_base(cls=ModelMeta)
diff --git a/src/mailman/database/schema/__init__.py b/src/mailman/database/schema/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/src/mailman/database/schema/__init__.py
+++ /dev/null
diff --git a/src/mailman/database/schema/helpers.py b/src/mailman/database/schema/helpers.py
deleted file mode 100644
index 827e6cc96..000000000
--- a/src/mailman/database/schema/helpers.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Copyright (C) 2013-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Schema migration helpers."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'make_listid',
- ]
-
-
-
-def make_listid(fqdn_listname):
- """Turn a FQDN list name into a List-ID."""
- list_name, at, mail_host = fqdn_listname.partition('@')
- if at == '':
- # If there is no @ sign in the value, assume it already contains the
- # list-id.
- return fqdn_listname
- return '{0}.{1}'.format(list_name, mail_host)
-
-
-
-def pivot(store, table_name):
- """Pivot a backup table into the real table name."""
- store.execute('DROP TABLE {}'.format(table_name))
- store.execute('ALTER TABLE {0}_backup RENAME TO {0}'.format(table_name))
diff --git a/src/mailman/database/schema/mm_00000000000000_base.py b/src/mailman/database/schema/mm_00000000000000_base.py
deleted file mode 100644
index ad085427f..000000000
--- a/src/mailman/database/schema/mm_00000000000000_base.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright (C) 2012-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Load the base schema."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'upgrade',
- ]
-
-
-VERSION = '00000000000000'
-_helper = None
-
-
-
-def upgrade(database, store, version, module_path):
- filename = '{0}.sql'.format(database.TAG)
- database.load_schema(store, version, filename, module_path)
diff --git a/src/mailman/database/schema/mm_20120407000000.py b/src/mailman/database/schema/mm_20120407000000.py
deleted file mode 100644
index 1d798ea96..000000000
--- a/src/mailman/database/schema/mm_20120407000000.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# Copyright (C) 2012-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""3.0b1 -> 3.0b2 schema migrations.
-
-All column changes are in the `mailinglist` table.
-
-* Renames:
- - news_prefix_subject_too -> nntp_prefix_subject_too
- - news_moderation -> newsgroup_moderation
-
-* Collapsing:
- - archive, archive_private -> archive_policy
-
-* Remove:
- - archive_volume_frequency
- - generic_nonmember_action
- - nntp_host
-
-* Added:
- - list_id
-
-* Changes:
- member.mailing_list holds the list_id not the fqdn_listname
-
-See https://bugs.launchpad.net/mailman/+bug/971013 for details.
-"""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'upgrade',
- ]
-
-
-from mailman.database.schema.helpers import pivot
-from mailman.interfaces.archiver import ArchivePolicy
-
-
-VERSION = '20120407000000'
-
-
-
-def upgrade(database, store, version, module_path):
- if database.TAG == 'sqlite':
- upgrade_sqlite(database, store, version, module_path)
- else:
- upgrade_postgres(database, store, version, module_path)
-
-
-
-def archive_policy(archive, archive_private):
- """Convert archive and archive_private to archive_policy."""
- if archive == 0:
- return ArchivePolicy.never.value
- elif archive_private == 1:
- return ArchivePolicy.private.value
- else:
- return ArchivePolicy.public.value
-
-
-
-def upgrade_sqlite(database, store, version, module_path):
- # Load the first part of the migration. This creates a temporary table to
- # hold the new mailinglist table columns. The problem is that some of the
- # changes must be performed in Python, so after the first part is loaded,
- # we do the Python changes, drop the old mailing list table, and then
- # rename the temporary table to its place.
- database.load_schema(
- store, version, 'sqlite_{0}_01.sql'.format(version), module_path)
- results = store.execute("""
- SELECT id, include_list_post_header,
- news_prefix_subject_too, news_moderation,
- archive, archive_private, list_name, mail_host
- FROM mailinglist;
- """)
- for value in results:
- (id, list_post,
- news_prefix, news_moderation,
- archive, archive_private,
- list_name, mail_host) = value
- # Figure out what the new archive_policy column value should be.
- list_id = '{0}.{1}'.format(list_name, mail_host)
- fqdn_listname = '{0}@{1}'.format(list_name, mail_host)
- store.execute("""
- UPDATE mailinglist_backup SET
- allow_list_posts = {0},
- newsgroup_moderation = {1},
- nntp_prefix_subject_too = {2},
- archive_policy = {3},
- list_id = '{4}'
- WHERE id = {5};
- """.format(
- list_post,
- news_moderation,
- news_prefix,
- archive_policy(archive, archive_private),
- list_id,
- id))
- # Also update the member.mailing_list column to hold the list_id
- # instead of the fqdn_listname.
- store.execute("""
- UPDATE member SET
- mailing_list = '{0}'
- WHERE mailing_list = '{1}';
- """.format(list_id, fqdn_listname))
- # Pivot the backup table to the real thing.
- pivot(store, 'mailinglist')
- # Now add some indexes that were previously missing.
- store.execute(
- 'CREATE INDEX ix_mailinglist_list_id ON mailinglist (list_id);')
- store.execute(
- 'CREATE INDEX ix_mailinglist_fqdn_listname '
- 'ON mailinglist (list_name, mail_host);')
- # Now, do the member table.
- results = store.execute('SELECT id, mailing_list FROM member;')
- for id, mailing_list in results:
- list_name, at, mail_host = mailing_list.partition('@')
- if at == '':
- list_id = mailing_list
- else:
- list_id = '{0}.{1}'.format(list_name, mail_host)
- store.execute("""
- UPDATE member_backup SET list_id = '{0}'
- WHERE id = {1};
- """.format(list_id, id))
- # Pivot the backup table to the real thing.
- pivot(store, 'member')
-
-
-
-def upgrade_postgres(database, store, version, module_path):
- # Get the old values from the mailinglist table.
- results = store.execute("""
- SELECT id, archive, archive_private, list_name, mail_host
- FROM mailinglist;
- """)
- # Do the simple renames first.
- store.execute("""
- ALTER TABLE mailinglist
- RENAME COLUMN news_prefix_subject_too TO nntp_prefix_subject_too;
- """)
- store.execute("""
- ALTER TABLE mailinglist
- RENAME COLUMN news_moderation TO newsgroup_moderation;
- """)
- store.execute("""
- ALTER TABLE mailinglist
- RENAME COLUMN include_list_post_header TO allow_list_posts;
- """)
- # Do the easy column drops next.
- for column in ('archive_volume_frequency',
- 'generic_nonmember_action',
- 'nntp_host'):
- store.execute(
- 'ALTER TABLE mailinglist DROP COLUMN {0};'.format(column))
- # Now do the trickier collapsing of values. Add the new columns.
- store.execute('ALTER TABLE mailinglist ADD COLUMN archive_policy INTEGER;')
- store.execute('ALTER TABLE mailinglist ADD COLUMN list_id TEXT;')
- # Query the database for the old values of archive and archive_private in
- # each column. Then loop through all the results and update the new
- # archive_policy from the old values.
- for value in results:
- id, archive, archive_private, list_name, mail_host = value
- list_id = '{0}.{1}'.format(list_name, mail_host)
- store.execute("""
- UPDATE mailinglist SET
- archive_policy = {0},
- list_id = '{1}'
- WHERE id = {2};
- """.format(archive_policy(archive, archive_private), list_id, id))
- # Now drop the old columns.
- for column in ('archive', 'archive_private'):
- store.execute(
- 'ALTER TABLE mailinglist DROP COLUMN {0};'.format(column))
- # Now add some indexes that were previously missing.
- store.execute(
- 'CREATE INDEX ix_mailinglist_list_id ON mailinglist (list_id);')
- store.execute(
- 'CREATE INDEX ix_mailinglist_fqdn_listname '
- 'ON mailinglist (list_name, mail_host);')
- # Now, do the member table.
- results = store.execute('SELECT id, mailing_list FROM member;')
- store.execute('ALTER TABLE member ADD COLUMN list_id TEXT;')
- for id, mailing_list in results:
- list_name, at, mail_host = mailing_list.partition('@')
- if at == '':
- list_id = mailing_list
- else:
- list_id = '{0}.{1}'.format(list_name, mail_host)
- store.execute("""
- UPDATE member SET list_id = '{0}'
- WHERE id = {1};
- """.format(list_id, id))
- store.execute('ALTER TABLE member DROP COLUMN mailing_list;')
- # Record the migration in the version table.
- database.load_schema(store, version, None, module_path)
diff --git a/src/mailman/database/schema/mm_20121015000000.py b/src/mailman/database/schema/mm_20121015000000.py
deleted file mode 100644
index 84510ff57..000000000
--- a/src/mailman/database/schema/mm_20121015000000.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Copyright (C) 2012-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""3.0b2 -> 3.0b3 schema migrations.
-
-Renamed:
- * bans.mailing_list -> bans.list_id
-
-Removed:
- * mailinglist.new_member_options
- * mailinglist.send_remindersn
-"""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'upgrade',
- ]
-
-
-from mailman.database.schema.helpers import make_listid, pivot
-
-
-VERSION = '20121015000000'
-
-
-
-def upgrade(database, store, version, module_path):
- if database.TAG == 'sqlite':
- upgrade_sqlite(database, store, version, module_path)
- else:
- upgrade_postgres(database, store, version, module_path)
-
-
-
-def upgrade_sqlite(database, store, version, module_path):
- database.load_schema(
- store, version, 'sqlite_{}_01.sql'.format(version), module_path)
- results = store.execute("""
- SELECT id, mailing_list
- FROM ban;
- """)
- for id, mailing_list in results:
- # Skip global bans since there's nothing to update.
- if mailing_list is None:
- continue
- store.execute("""
- UPDATE ban_backup SET list_id = '{}'
- WHERE id = {};
- """.format(make_listid(mailing_list), id))
- # Pivot the bans backup table to the real thing.
- pivot(store, 'ban')
- pivot(store, 'mailinglist')
-
-
-
-def upgrade_postgres(database, store, version, module_path):
- # Get the old values from the ban table.
- results = store.execute('SELECT id, mailing_list FROM ban;')
- store.execute('ALTER TABLE ban ADD COLUMN list_id TEXT;')
- for id, mailing_list in results:
- # Skip global bans since there's nothing to update.
- if mailing_list is None:
- continue
- store.execute("""
- UPDATE ban SET list_id = '{0}'
- WHERE id = {1};
- """.format(make_listid(mailing_list), id))
- store.execute('ALTER TABLE ban DROP COLUMN mailing_list;')
- store.execute('ALTER TABLE mailinglist DROP COLUMN new_member_options;')
- store.execute('ALTER TABLE mailinglist DROP COLUMN send_reminders;')
- store.execute('ALTER TABLE mailinglist DROP COLUMN subscribe_policy;')
- store.execute('ALTER TABLE mailinglist DROP COLUMN unsubscribe_policy;')
- store.execute(
- 'ALTER TABLE mailinglist DROP COLUMN subscribe_auto_approval;')
- store.execute('ALTER TABLE mailinglist DROP COLUMN private_roster;')
- store.execute(
- 'ALTER TABLE mailinglist DROP COLUMN admin_member_chunksize;')
- # Record the migration in the version table.
- database.load_schema(store, version, None, module_path)
diff --git a/src/mailman/database/schema/mm_20130406000000.py b/src/mailman/database/schema/mm_20130406000000.py
deleted file mode 100644
index 8d38dbab0..000000000
--- a/src/mailman/database/schema/mm_20130406000000.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Copyright (C) 2013-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""3.0b3 -> 3.0b4 schema migrations.
-
-Renamed:
- * bounceevent.list_name -> bounceevent.list_id
-"""
-
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'upgrade'
- ]
-
-
-from mailman.database.schema.helpers import make_listid, pivot
-
-
-VERSION = '20130406000000'
-
-
-
-def upgrade(database, store, version, module_path):
- if database.TAG == 'sqlite':
- upgrade_sqlite(database, store, version, module_path)
- else:
- upgrade_postgres(database, store, version, module_path)
-
-
-
-def upgrade_sqlite(database, store, version, module_path):
- database.load_schema(
- store, version, 'sqlite_{}_01.sql'.format(version), module_path)
- results = store.execute("""
- SELECT id, list_name
- FROM bounceevent;
- """)
- for id, list_name in results:
- store.execute("""
- UPDATE bounceevent_backup SET list_id = '{}'
- WHERE id = {};
- """.format(make_listid(list_name), id))
- pivot(store, 'bounceevent')
-
-
-
-def upgrade_postgres(database, store, version, module_path):
- pass
diff --git a/src/mailman/database/schema/postgres.sql b/src/mailman/database/schema/postgres.sql
deleted file mode 100644
index 0e97a4332..000000000
--- a/src/mailman/database/schema/postgres.sql
+++ /dev/null
@@ -1,349 +0,0 @@
-CREATE TABLE mailinglist (
- id SERIAL NOT NULL,
- -- List identity
- list_name TEXT,
- mail_host TEXT,
- include_list_post_header BOOLEAN,
- include_rfc2369_headers BOOLEAN,
- -- Attributes not directly modifiable via the web u/i
- created_at TIMESTAMP,
- admin_member_chunksize INTEGER,
- next_request_id INTEGER,
- next_digest_number INTEGER,
- digest_last_sent_at TIMESTAMP,
- volume INTEGER,
- last_post_at TIMESTAMP,
- accept_these_nonmembers BYTEA,
- acceptable_aliases_id INTEGER,
- admin_immed_notify BOOLEAN,
- admin_notify_mchanges BOOLEAN,
- administrivia BOOLEAN,
- advertised BOOLEAN,
- anonymous_list BOOLEAN,
- archive BOOLEAN,
- archive_private BOOLEAN,
- archive_volume_frequency INTEGER,
- -- Automatic responses.
- autorespond_owner INTEGER,
- autoresponse_owner_text TEXT,
- autorespond_postings INTEGER,
- autoresponse_postings_text TEXT,
- autorespond_requests INTEGER,
- autoresponse_request_text TEXT,
- autoresponse_grace_period TEXT,
- -- Bounces.
- forward_unrecognized_bounces_to INTEGER,
- process_bounces BOOLEAN,
- bounce_info_stale_after TEXT,
- bounce_matching_headers TEXT,
- bounce_notify_owner_on_disable BOOLEAN,
- bounce_notify_owner_on_removal BOOLEAN,
- bounce_score_threshold INTEGER,
- bounce_you_are_disabled_warnings INTEGER,
- bounce_you_are_disabled_warnings_interval TEXT,
- -- Content filtering.
- filter_action INTEGER,
- filter_content BOOLEAN,
- collapse_alternatives BOOLEAN,
- convert_html_to_plaintext BOOLEAN,
- default_member_action INTEGER,
- default_nonmember_action INTEGER,
- description TEXT,
- digest_footer_uri TEXT,
- digest_header_uri TEXT,
- digest_is_default BOOLEAN,
- digest_send_periodic BOOLEAN,
- digest_size_threshold REAL,
- digest_volume_frequency INTEGER,
- digestable BOOLEAN,
- discard_these_nonmembers BYTEA,
- emergency BOOLEAN,
- encode_ascii_prefixes BOOLEAN,
- first_strip_reply_to BOOLEAN,
- footer_uri TEXT,
- forward_auto_discards BOOLEAN,
- gateway_to_mail BOOLEAN,
- gateway_to_news BOOLEAN,
- generic_nonmember_action INTEGER,
- goodbye_message_uri TEXT,
- header_matches BYTEA,
- header_uri TEXT,
- hold_these_nonmembers BYTEA,
- info TEXT,
- linked_newsgroup TEXT,
- max_days_to_hold INTEGER,
- max_message_size INTEGER,
- max_num_recipients INTEGER,
- member_moderation_notice TEXT,
- mime_is_default_digest BOOLEAN,
- moderator_password TEXT,
- new_member_options INTEGER,
- news_moderation INTEGER,
- news_prefix_subject_too BOOLEAN,
- nntp_host TEXT,
- nondigestable BOOLEAN,
- nonmember_rejection_notice TEXT,
- obscure_addresses BOOLEAN,
- owner_chain TEXT,
- owner_pipeline TEXT,
- personalize INTEGER,
- post_id INTEGER,
- posting_chain TEXT,
- posting_pipeline TEXT,
- preferred_language TEXT,
- private_roster BOOLEAN,
- display_name TEXT,
- reject_these_nonmembers BYTEA,
- reply_goes_to_list INTEGER,
- reply_to_address TEXT,
- require_explicit_destination BOOLEAN,
- respond_to_post_requests BOOLEAN,
- scrub_nondigest BOOLEAN,
- send_goodbye_message BOOLEAN,
- send_reminders BOOLEAN,
- send_welcome_message BOOLEAN,
- subject_prefix TEXT,
- subscribe_auto_approval BYTEA,
- subscribe_policy INTEGER,
- topics BYTEA,
- topics_bodylines_limit INTEGER,
- topics_enabled BOOLEAN,
- unsubscribe_policy INTEGER,
- welcome_message_uri TEXT,
- -- This was accidentally added by the PostgreSQL porter.
- -- moderation_callback TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE _request (
- id SERIAL NOT NULL,
- "key" TEXT,
- request_type INTEGER,
- data_hash BYTEA,
- mailing_list_id INTEGER,
- PRIMARY KEY (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT _request_mailing_list_id_fk
- -- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-
-CREATE TABLE acceptablealias (
- id SERIAL NOT NULL,
- "alias" TEXT NOT NULL,
- mailing_list_id INTEGER NOT NULL,
- PRIMARY KEY (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT acceptablealias_mailing_list_id_fk
- -- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-CREATE INDEX ix_acceptablealias_mailing_list_id
- ON acceptablealias (mailing_list_id);
-CREATE INDEX ix_acceptablealias_alias ON acceptablealias ("alias");
-
-CREATE TABLE preferences (
- id SERIAL NOT NULL,
- acknowledge_posts BOOLEAN,
- hide_address BOOLEAN,
- preferred_language TEXT,
- receive_list_copy BOOLEAN,
- receive_own_postings BOOLEAN,
- delivery_mode INTEGER,
- delivery_status INTEGER,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE address (
- id SERIAL NOT NULL,
- email TEXT,
- _original TEXT,
- display_name TEXT,
- verified_on TIMESTAMP,
- registered_on TIMESTAMP,
- user_id INTEGER,
- preferences_id INTEGER,
- PRIMARY KEY (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT address_preferences_id_fk
- -- FOREIGN KEY (preferences_id) REFERENCES preferences (id)
- );
-
-CREATE TABLE "user" (
- id SERIAL NOT NULL,
- display_name TEXT,
- password BYTEA,
- _user_id UUID,
- _created_on TIMESTAMP,
- _preferred_address_id INTEGER,
- preferences_id INTEGER,
- PRIMARY KEY (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT user_preferences_id_fk
- -- FOREIGN KEY (preferences_id) REFERENCES preferences (id),
- -- XXX: config.db_reset() triggers IntegrityError
- -- CONSTRAINT _preferred_address_id_fk
- -- FOREIGN KEY (_preferred_address_id) REFERENCES address (id)
- );
-CREATE INDEX ix_user_user_id ON "user" (_user_id);
-
--- since user and address have circular foreign key refs, the
--- constraint on the address table has to be added after
--- the user table is created
---
--- XXX: users.rst triggers an IntegrityError
--- ALTER TABLE address ADD
--- CONSTRAINT address_user_id_fk
--- FOREIGN KEY (user_id) REFERENCES "user" (id);
-
-CREATE TABLE autoresponserecord (
- id SERIAL NOT NULL,
- address_id INTEGER,
- mailing_list_id INTEGER,
- response_type INTEGER,
- date_sent TIMESTAMP,
- PRIMARY KEY (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT autoresponserecord_address_id_fk
- -- FOREIGN KEY (address_id) REFERENCES address (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT autoresponserecord_mailing_list_id
- -- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-CREATE INDEX ix_autoresponserecord_address_id
- ON autoresponserecord (address_id);
-CREATE INDEX ix_autoresponserecord_mailing_list_id
- ON autoresponserecord (mailing_list_id);
-
-CREATE TABLE bounceevent (
- id SERIAL NOT NULL,
- list_name TEXT,
- email TEXT,
- "timestamp" TIMESTAMP,
- message_id TEXT,
- context INTEGER,
- processed BOOLEAN,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE contentfilter (
- id SERIAL NOT NULL,
- mailing_list_id INTEGER,
- filter_pattern TEXT,
- filter_type INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT contentfilter_mailing_list_id
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-CREATE INDEX ix_contentfilter_mailing_list_id
- ON contentfilter (mailing_list_id);
-
-CREATE TABLE domain (
- id SERIAL NOT NULL,
- mail_host TEXT,
- base_url TEXT,
- description TEXT,
- contact_address TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE language (
- id SERIAL NOT NULL,
- code TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE member (
- id SERIAL NOT NULL,
- _member_id UUID,
- role INTEGER,
- mailing_list TEXT,
- moderation_action INTEGER,
- address_id INTEGER,
- preferences_id INTEGER,
- user_id INTEGER,
- PRIMARY KEY (id)
- -- XXX: config.db_reset() triggers IntegrityError
- -- ,
- -- CONSTRAINT member_address_id_fk
- -- FOREIGN KEY (address_id) REFERENCES address (id),
- -- XXX: config.db_reset() triggers IntegrityError
- -- CONSTRAINT member_preferences_id_fk
- -- FOREIGN KEY (preferences_id) REFERENCES preferences (id),
- -- CONSTRAINT member_user_id_fk
- -- FOREIGN KEY (user_id) REFERENCES "user" (id)
- );
-CREATE INDEX ix_member__member_id ON member (_member_id);
-CREATE INDEX ix_member_address_id ON member (address_id);
-CREATE INDEX ix_member_preferences_id ON member (preferences_id);
-
-CREATE TABLE message (
- id SERIAL NOT NULL,
- message_id_hash BYTEA,
- path BYTEA,
- message_id TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE onelastdigest (
- id SERIAL NOT NULL,
- mailing_list_id INTEGER,
- address_id INTEGER,
- delivery_mode INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT onelastdigest_mailing_list_id_fk
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist(id),
- CONSTRAINT onelastdigest_address_id_fk
- FOREIGN KEY (address_id) REFERENCES address(id)
- );
-
-CREATE TABLE pended (
- id SERIAL NOT NULL,
- token BYTEA,
- expiration_date TIMESTAMP,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE pendedkeyvalue (
- id SERIAL NOT NULL,
- "key" TEXT,
- value TEXT,
- pended_id INTEGER,
- PRIMARY KEY (id)
- -- ,
- -- XXX: config.db_reset() triggers IntegrityError
- -- CONSTRAINT pendedkeyvalue_pended_id_fk
- -- FOREIGN KEY (pended_id) REFERENCES pended (id)
- );
-
-CREATE TABLE version (
- id SERIAL NOT NULL,
- component TEXT,
- version TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE INDEX ix__request_mailing_list_id ON _request (mailing_list_id);
-CREATE INDEX ix_address_preferences_id ON address (preferences_id);
-CREATE INDEX ix_address_user_id ON address (user_id);
-CREATE INDEX ix_pendedkeyvalue_pended_id ON pendedkeyvalue (pended_id);
-CREATE INDEX ix_user_preferences_id ON "user" (preferences_id);
-
-CREATE TABLE ban (
- id SERIAL NOT NULL,
- email TEXT,
- mailing_list TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE uid (
- -- Keep track of all assigned unique ids to prevent re-use.
- id SERIAL NOT NULL,
- uid UUID,
- PRIMARY KEY (id)
- );
-CREATE INDEX ix_uid_uid ON uid (uid);
diff --git a/src/mailman/database/schema/sqlite.sql b/src/mailman/database/schema/sqlite.sql
deleted file mode 100644
index e2b2d3814..000000000
--- a/src/mailman/database/schema/sqlite.sql
+++ /dev/null
@@ -1,327 +0,0 @@
--- THIS FILE HAS BEEN FROZEN AS OF 3.0b1
--- SEE THE SCHEMA MIGRATIONS FOR DIFFERENCES.
-
-PRAGMA foreign_keys = ON;
-
-CREATE TABLE _request (
- id INTEGER NOT NULL,
- "key" TEXT,
- request_type INTEGER,
- data_hash TEXT,
- mailing_list_id INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT _request_mailing_list_id_fk
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-
-CREATE TABLE acceptablealias (
- id INTEGER NOT NULL,
- "alias" TEXT NOT NULL,
- mailing_list_id INTEGER NOT NULL,
- PRIMARY KEY (id),
- CONSTRAINT acceptablealias_mailing_list_id_fk
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-CREATE INDEX ix_acceptablealias_mailing_list_id
- ON acceptablealias (mailing_list_id);
-CREATE INDEX ix_acceptablealias_alias ON acceptablealias ("alias");
-
-CREATE TABLE address (
- id INTEGER NOT NULL,
- email TEXT,
- _original TEXT,
- display_name TEXT,
- verified_on TIMESTAMP,
- registered_on TIMESTAMP,
- user_id INTEGER,
- preferences_id INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT address_user_id_fk
- FOREIGN KEY (user_id) REFERENCES user (id),
- CONSTRAINT address_preferences_id_fk
- FOREIGN KEY (preferences_id) REFERENCES preferences (id)
- );
-
-CREATE TABLE autoresponserecord (
- id INTEGER NOT NULL,
- address_id INTEGER,
- mailing_list_id INTEGER,
- response_type INTEGER,
- date_sent TIMESTAMP,
- PRIMARY KEY (id),
- CONSTRAINT autoresponserecord_address_id_fk
- FOREIGN KEY (address_id) REFERENCES address (id),
- CONSTRAINT autoresponserecord_mailing_list_id
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-CREATE INDEX ix_autoresponserecord_address_id
- ON autoresponserecord (address_id);
-CREATE INDEX ix_autoresponserecord_mailing_list_id
- ON autoresponserecord (mailing_list_id);
-
-CREATE TABLE bounceevent (
- id INTEGER NOT NULL,
- list_name TEXT,
- email TEXT,
- 'timestamp' TIMESTAMP,
- message_id TEXT,
- context INTEGER,
- processed BOOLEAN,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE contentfilter (
- id INTEGER NOT NULL,
- mailing_list_id INTEGER,
- filter_pattern TEXT,
- filter_type INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT contentfilter_mailing_list_id
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist (id)
- );
-CREATE INDEX ix_contentfilter_mailing_list_id
- ON contentfilter (mailing_list_id);
-
-CREATE TABLE domain (
- id INTEGER NOT NULL,
- mail_host TEXT,
- base_url TEXT,
- description TEXT,
- contact_address TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE language (
- id INTEGER NOT NULL,
- code TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE mailinglist (
- id INTEGER NOT NULL,
- -- List identity
- list_name TEXT,
- mail_host TEXT,
- include_list_post_header BOOLEAN,
- include_rfc2369_headers BOOLEAN,
- -- Attributes not directly modifiable via the web u/i
- created_at TIMESTAMP,
- admin_member_chunksize INTEGER,
- next_request_id INTEGER,
- next_digest_number INTEGER,
- digest_last_sent_at TIMESTAMP,
- volume INTEGER,
- last_post_at TIMESTAMP,
- accept_these_nonmembers BLOB,
- acceptable_aliases_id INTEGER,
- admin_immed_notify BOOLEAN,
- admin_notify_mchanges BOOLEAN,
- administrivia BOOLEAN,
- advertised BOOLEAN,
- anonymous_list BOOLEAN,
- archive BOOLEAN,
- archive_private BOOLEAN,
- archive_volume_frequency INTEGER,
- -- Automatic responses.
- autorespond_owner INTEGER,
- autoresponse_owner_text TEXT,
- autorespond_postings INTEGER,
- autoresponse_postings_text TEXT,
- autorespond_requests INTEGER,
- autoresponse_request_text TEXT,
- autoresponse_grace_period TEXT,
- -- Bounces.
- forward_unrecognized_bounces_to INTEGER,
- process_bounces BOOLEAN,
- bounce_info_stale_after TEXT,
- bounce_matching_headers TEXT,
- bounce_notify_owner_on_disable BOOLEAN,
- bounce_notify_owner_on_removal BOOLEAN,
- bounce_score_threshold INTEGER,
- bounce_you_are_disabled_warnings INTEGER,
- bounce_you_are_disabled_warnings_interval TEXT,
- -- Content filtering.
- filter_action INTEGER,
- filter_content BOOLEAN,
- collapse_alternatives BOOLEAN,
- convert_html_to_plaintext BOOLEAN,
- default_member_action INTEGER,
- default_nonmember_action INTEGER,
- description TEXT,
- digest_footer_uri TEXT,
- digest_header_uri TEXT,
- digest_is_default BOOLEAN,
- digest_send_periodic BOOLEAN,
- digest_size_threshold FLOAT,
- digest_volume_frequency INTEGER,
- digestable BOOLEAN,
- discard_these_nonmembers BLOB,
- emergency BOOLEAN,
- encode_ascii_prefixes BOOLEAN,
- first_strip_reply_to BOOLEAN,
- footer_uri TEXT,
- forward_auto_discards BOOLEAN,
- gateway_to_mail BOOLEAN,
- gateway_to_news BOOLEAN,
- generic_nonmember_action INTEGER,
- goodbye_message_uri TEXT,
- header_matches BLOB,
- header_uri TEXT,
- hold_these_nonmembers BLOB,
- info TEXT,
- linked_newsgroup TEXT,
- max_days_to_hold INTEGER,
- max_message_size INTEGER,
- max_num_recipients INTEGER,
- member_moderation_notice TEXT,
- mime_is_default_digest BOOLEAN,
- moderator_password TEXT,
- new_member_options INTEGER,
- news_moderation INTEGER,
- news_prefix_subject_too BOOLEAN,
- nntp_host TEXT,
- nondigestable BOOLEAN,
- nonmember_rejection_notice TEXT,
- obscure_addresses BOOLEAN,
- owner_chain TEXT,
- owner_pipeline TEXT,
- personalize INTEGER,
- post_id INTEGER,
- posting_chain TEXT,
- posting_pipeline TEXT,
- preferred_language TEXT,
- private_roster BOOLEAN,
- display_name TEXT,
- reject_these_nonmembers BLOB,
- reply_goes_to_list INTEGER,
- reply_to_address TEXT,
- require_explicit_destination BOOLEAN,
- respond_to_post_requests BOOLEAN,
- scrub_nondigest BOOLEAN,
- send_goodbye_message BOOLEAN,
- send_reminders BOOLEAN,
- send_welcome_message BOOLEAN,
- subject_prefix TEXT,
- subscribe_auto_approval BLOB,
- subscribe_policy INTEGER,
- topics BLOB,
- topics_bodylines_limit INTEGER,
- topics_enabled BOOLEAN,
- unsubscribe_policy INTEGER,
- welcome_message_uri TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE member (
- id INTEGER NOT NULL,
- _member_id TEXT,
- role INTEGER,
- mailing_list TEXT,
- moderation_action INTEGER,
- address_id INTEGER,
- preferences_id INTEGER,
- user_id INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT member_address_id_fk
- FOREIGN KEY (address_id) REFERENCES address (id),
- CONSTRAINT member_preferences_id_fk
- FOREIGN KEY (preferences_id) REFERENCES preferences (id)
- CONSTRAINT member_user_id_fk
- FOREIGN KEY (user_id) REFERENCES user (id)
- );
-CREATE INDEX ix_member__member_id ON member (_member_id);
-CREATE INDEX ix_member_address_id ON member (address_id);
-CREATE INDEX ix_member_preferences_id ON member (preferences_id);
-
-CREATE TABLE message (
- id INTEGER NOT NULL,
- message_id_hash TEXT,
- path TEXT,
- message_id TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE onelastdigest (
- id INTEGER NOT NULL,
- mailing_list_id INTEGER,
- address_id INTEGER,
- delivery_mode INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT onelastdigest_mailing_list_id_fk
- FOREIGN KEY (mailing_list_id) REFERENCES mailinglist(id),
- CONSTRAINT onelastdigest_address_id_fk
- FOREIGN KEY (address_id) REFERENCES address(id)
- );
-
-CREATE TABLE pended (
- id INTEGER NOT NULL,
- token TEXT,
- expiration_date TIMESTAMP,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE pendedkeyvalue (
- id INTEGER NOT NULL,
- "key" TEXT,
- value TEXT,
- pended_id INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT pendedkeyvalue_pended_id_fk
- FOREIGN KEY (pended_id) REFERENCES pended (id)
- );
-
-CREATE TABLE preferences (
- id INTEGER NOT NULL,
- acknowledge_posts BOOLEAN,
- hide_address BOOLEAN,
- preferred_language TEXT,
- receive_list_copy BOOLEAN,
- receive_own_postings BOOLEAN,
- delivery_mode INTEGER,
- delivery_status INTEGER,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE user (
- id INTEGER NOT NULL,
- display_name TEXT,
- password BINARY,
- _user_id TEXT,
- _created_on TIMESTAMP,
- _preferred_address_id INTEGER,
- preferences_id INTEGER,
- PRIMARY KEY (id),
- CONSTRAINT user_preferences_id_fk
- FOREIGN KEY (preferences_id) REFERENCES preferences (id),
- CONSTRAINT _preferred_address_id_fk
- FOREIGN KEY (_preferred_address_id) REFERENCES address (id)
- );
-CREATE INDEX ix_user_user_id ON user (_user_id);
-
-CREATE TABLE version (
- id INTEGER NOT NULL,
- component TEXT,
- version TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE INDEX ix__request_mailing_list_id ON _request (mailing_list_id);
-CREATE INDEX ix_address_preferences_id ON address (preferences_id);
-CREATE INDEX ix_address_user_id ON address (user_id);
-CREATE INDEX ix_pendedkeyvalue_pended_id ON pendedkeyvalue (pended_id);
-CREATE INDEX ix_user_preferences_id ON user (preferences_id);
-
-CREATE TABLE ban (
- id INTEGER NOT NULL,
- email TEXT,
- mailing_list TEXT,
- PRIMARY KEY (id)
- );
-
-CREATE TABLE uid (
- -- Keep track of all assigned unique ids to prevent re-use.
- id INTEGER NOT NULL,
- uid TEXT,
- PRIMARY KEY (id)
- );
-CREATE INDEX ix_uid_uid ON uid (uid);
diff --git a/src/mailman/database/schema/sqlite_20120407000000_01.sql b/src/mailman/database/schema/sqlite_20120407000000_01.sql
deleted file mode 100644
index a8db75be9..000000000
--- a/src/mailman/database/schema/sqlite_20120407000000_01.sql
+++ /dev/null
@@ -1,280 +0,0 @@
--- This file contains the sqlite3 schema migration from
--- 3.0b1 TO 3.0b2
---
--- 3.0b2 has been released thus you MAY NOT edit this file.
-
--- For SQLite3 migration strategy, see
--- http://sqlite.org/faq.html#q11
-
--- REMOVALS from the mailinglist table:
--- REM archive
--- REM archive_private
--- REM archive_volume_frequency
--- REM include_list_post_header
--- REM news_moderation
--- REM news_prefix_subject_too
--- REM nntp_host
---
--- ADDS to the mailing list table:
--- ADD allow_list_posts
--- ADD archive_policy
--- ADD list_id
--- ADD newsgroup_moderation
--- ADD nntp_prefix_subject_too
-
--- LP: #971013
--- LP: #967238
-
--- REMOVALS from the member table:
--- REM mailing_list
-
--- ADDS to the member table:
--- ADD list_id
-
--- LP: #1024509
-
-
-CREATE TABLE mailinglist_backup (
- id INTEGER NOT NULL,
- -- List identity
- list_name TEXT,
- mail_host TEXT,
- allow_list_posts BOOLEAN,
- include_rfc2369_headers BOOLEAN,
- -- Attributes not directly modifiable via the web u/i
- created_at TIMESTAMP,
- admin_member_chunksize INTEGER,
- next_request_id INTEGER,
- next_digest_number INTEGER,
- digest_last_sent_at TIMESTAMP,
- volume INTEGER,
- last_post_at TIMESTAMP,
- accept_these_nonmembers BLOB,
- acceptable_aliases_id INTEGER,
- admin_immed_notify BOOLEAN,
- admin_notify_mchanges BOOLEAN,
- administrivia BOOLEAN,
- advertised BOOLEAN,
- anonymous_list BOOLEAN,
- -- Automatic responses.
- autorespond_owner INTEGER,
- autoresponse_owner_text TEXT,
- autorespond_postings INTEGER,
- autoresponse_postings_text TEXT,
- autorespond_requests INTEGER,
- autoresponse_request_text TEXT,
- autoresponse_grace_period TEXT,
- -- Bounces.
- forward_unrecognized_bounces_to INTEGER,
- process_bounces BOOLEAN,
- bounce_info_stale_after TEXT,
- bounce_matching_headers TEXT,
- bounce_notify_owner_on_disable BOOLEAN,
- bounce_notify_owner_on_removal BOOLEAN,
- bounce_score_threshold INTEGER,
- bounce_you_are_disabled_warnings INTEGER,
- bounce_you_are_disabled_warnings_interval TEXT,
- -- Content filtering.
- filter_action INTEGER,
- filter_content BOOLEAN,
- collapse_alternatives BOOLEAN,
- convert_html_to_plaintext BOOLEAN,
- default_member_action INTEGER,
- default_nonmember_action INTEGER,
- description TEXT,
- digest_footer_uri TEXT,
- digest_header_uri TEXT,
- digest_is_default BOOLEAN,
- digest_send_periodic BOOLEAN,
- digest_size_threshold FLOAT,
- digest_volume_frequency INTEGER,
- digestable BOOLEAN,
- discard_these_nonmembers BLOB,
- emergency BOOLEAN,
- encode_ascii_prefixes BOOLEAN,
- first_strip_reply_to BOOLEAN,
- footer_uri TEXT,
- forward_auto_discards BOOLEAN,
- gateway_to_mail BOOLEAN,
- gateway_to_news BOOLEAN,
- goodbye_message_uri TEXT,
- header_matches BLOB,
- header_uri TEXT,
- hold_these_nonmembers BLOB,
- info TEXT,
- linked_newsgroup TEXT,
- max_days_to_hold INTEGER,
- max_message_size INTEGER,
- max_num_recipients INTEGER,
- member_moderation_notice TEXT,
- mime_is_default_digest BOOLEAN,
- moderator_password TEXT,
- new_member_options INTEGER,
- nondigestable BOOLEAN,
- nonmember_rejection_notice TEXT,
- obscure_addresses BOOLEAN,
- owner_chain TEXT,
- owner_pipeline TEXT,
- personalize INTEGER,
- post_id INTEGER,
- posting_chain TEXT,
- posting_pipeline TEXT,
- preferred_language TEXT,
- private_roster BOOLEAN,
- display_name TEXT,
- reject_these_nonmembers BLOB,
- reply_goes_to_list INTEGER,
- reply_to_address TEXT,
- require_explicit_destination BOOLEAN,
- respond_to_post_requests BOOLEAN,
- scrub_nondigest BOOLEAN,
- send_goodbye_message BOOLEAN,
- send_reminders BOOLEAN,
- send_welcome_message BOOLEAN,
- subject_prefix TEXT,
- subscribe_auto_approval BLOB,
- subscribe_policy INTEGER,
- topics BLOB,
- topics_bodylines_limit INTEGER,
- topics_enabled BOOLEAN,
- unsubscribe_policy INTEGER,
- welcome_message_uri TEXT,
- PRIMARY KEY (id)
- );
-
-INSERT INTO mailinglist_backup SELECT
- id,
- -- List identity
- list_name,
- mail_host,
- include_list_post_header,
- include_rfc2369_headers,
- -- Attributes not directly modifiable via the web u/i
- created_at,
- admin_member_chunksize,
- next_request_id,
- next_digest_number,
- digest_last_sent_at,
- volume,
- last_post_at,
- accept_these_nonmembers,
- acceptable_aliases_id,
- admin_immed_notify,
- admin_notify_mchanges,
- administrivia,
- advertised,
- anonymous_list,
- -- Automatic responses.
- autorespond_owner,
- autoresponse_owner_text,
- autorespond_postings,
- autoresponse_postings_text,
- autorespond_requests,
- autoresponse_request_text,
- autoresponse_grace_period,
- -- Bounces.
- forward_unrecognized_bounces_to,
- process_bounces,
- bounce_info_stale_after,
- bounce_matching_headers,
- bounce_notify_owner_on_disable,
- bounce_notify_owner_on_removal,
- bounce_score_threshold,
- bounce_you_are_disabled_warnings,
- bounce_you_are_disabled_warnings_interval,
- -- Content filtering.
- filter_action,
- filter_content,
- collapse_alternatives,
- convert_html_to_plaintext,
- default_member_action,
- default_nonmember_action,
- description,
- digest_footer_uri,
- digest_header_uri,
- digest_is_default,
- digest_send_periodic,
- digest_size_threshold,
- digest_volume_frequency,
- digestable,
- discard_these_nonmembers,
- emergency,
- encode_ascii_prefixes,
- first_strip_reply_to,
- footer_uri,
- forward_auto_discards,
- gateway_to_mail,
- gateway_to_news,
- goodbye_message_uri,
- header_matches,
- header_uri,
- hold_these_nonmembers,
- info,
- linked_newsgroup,
- max_days_to_hold,
- max_message_size,
- max_num_recipients,
- member_moderation_notice,
- mime_is_default_digest,
- moderator_password,
- new_member_options,
- nondigestable,
- nonmember_rejection_notice,
- obscure_addresses,
- owner_chain,
- owner_pipeline,
- personalize,
- post_id,
- posting_chain,
- posting_pipeline,
- preferred_language,
- private_roster,
- display_name,
- reject_these_nonmembers,
- reply_goes_to_list,
- reply_to_address,
- require_explicit_destination,
- respond_to_post_requests,
- scrub_nondigest,
- send_goodbye_message,
- send_reminders,
- send_welcome_message,
- subject_prefix,
- subscribe_auto_approval,
- subscribe_policy,
- topics,
- topics_bodylines_limit,
- topics_enabled,
- unsubscribe_policy,
- welcome_message_uri
- FROM mailinglist;
-
-CREATE TABLE member_backup(
- id INTEGER NOT NULL,
- _member_id TEXT,
- role INTEGER,
- moderation_action INTEGER,
- address_id INTEGER,
- preferences_id INTEGER,
- user_id INTEGER,
- PRIMARY KEY (id)
- );
-
-INSERT INTO member_backup SELECT
- id,
- _member_id,
- role,
- moderation_action,
- address_id,
- preferences_id,
- user_id
- FROM member;
-
-
--- Add the new columns. They'll get inserted at the Python layer.
-ALTER TABLE mailinglist_backup ADD COLUMN archive_policy INTEGER;
-ALTER TABLE mailinglist_backup ADD COLUMN list_id TEXT;
-ALTER TABLE mailinglist_backup ADD COLUMN nntp_prefix_subject_too INTEGER;
-ALTER TABLE mailinglist_backup ADD COLUMN newsgroup_moderation INTEGER;
-
-ALTER TABLE member_backup ADD COLUMN list_id TEXT;
diff --git a/src/mailman/database/schema/sqlite_20121015000000_01.sql b/src/mailman/database/schema/sqlite_20121015000000_01.sql
deleted file mode 100644
index a80dc03df..000000000
--- a/src/mailman/database/schema/sqlite_20121015000000_01.sql
+++ /dev/null
@@ -1,230 +0,0 @@
--- This file contains the sqlite3 schema migration from
--- 3.0b2 TO 3.0b3
---
--- 3.0b3 has been released thus you MAY NOT edit this file.
-
--- REMOVALS from the ban table:
--- REM mailing_list
-
--- ADDS to the ban table:
--- ADD list_id
-
-CREATE TABLE ban_backup (
- id INTEGER NOT NULL,
- email TEXT,
- PRIMARY KEY (id)
- );
-
-INSERT INTO ban_backup SELECT
- id, email
- FROM ban;
-
-ALTER TABLE ban_backup ADD COLUMN list_id TEXT;
-
--- REMOVALS from the mailinglist table.
--- REM new_member_options
--- REM send_reminders
--- REM subscribe_policy
--- REM unsubscribe_policy
--- REM subscribe_auto_approval
--- REM private_roster
--- REM admin_member_chunksize
-
-CREATE TABLE mailinglist_backup (
- id INTEGER NOT NULL,
- list_name TEXT,
- mail_host TEXT,
- allow_list_posts BOOLEAN,
- include_rfc2369_headers BOOLEAN,
- created_at TIMESTAMP,
- next_request_id INTEGER,
- next_digest_number INTEGER,
- digest_last_sent_at TIMESTAMP,
- volume INTEGER,
- last_post_at TIMESTAMP,
- accept_these_nonmembers BLOB,
- acceptable_aliases_id INTEGER,
- admin_immed_notify BOOLEAN,
- admin_notify_mchanges BOOLEAN,
- administrivia BOOLEAN,
- advertised BOOLEAN,
- anonymous_list BOOLEAN,
- autorespond_owner INTEGER,
- autoresponse_owner_text TEXT,
- autorespond_postings INTEGER,
- autoresponse_postings_text TEXT,
- autorespond_requests INTEGER,
- autoresponse_request_text TEXT,
- autoresponse_grace_period TEXT,
- forward_unrecognized_bounces_to INTEGER,
- process_bounces BOOLEAN,
- bounce_info_stale_after TEXT,
- bounce_matching_headers TEXT,
- bounce_notify_owner_on_disable BOOLEAN,
- bounce_notify_owner_on_removal BOOLEAN,
- bounce_score_threshold INTEGER,
- bounce_you_are_disabled_warnings INTEGER,
- bounce_you_are_disabled_warnings_interval TEXT,
- filter_action INTEGER,
- filter_content BOOLEAN,
- collapse_alternatives BOOLEAN,
- convert_html_to_plaintext BOOLEAN,
- default_member_action INTEGER,
- default_nonmember_action INTEGER,
- description TEXT,
- digest_footer_uri TEXT,
- digest_header_uri TEXT,
- digest_is_default BOOLEAN,
- digest_send_periodic BOOLEAN,
- digest_size_threshold FLOAT,
- digest_volume_frequency INTEGER,
- digestable BOOLEAN,
- discard_these_nonmembers BLOB,
- emergency BOOLEAN,
- encode_ascii_prefixes BOOLEAN,
- first_strip_reply_to BOOLEAN,
- footer_uri TEXT,
- forward_auto_discards BOOLEAN,
- gateway_to_mail BOOLEAN,
- gateway_to_news BOOLEAN,
- goodbye_message_uri TEXT,
- header_matches BLOB,
- header_uri TEXT,
- hold_these_nonmembers BLOB,
- info TEXT,
- linked_newsgroup TEXT,
- max_days_to_hold INTEGER,
- max_message_size INTEGER,
- max_num_recipients INTEGER,
- member_moderation_notice TEXT,
- mime_is_default_digest BOOLEAN,
- moderator_password TEXT,
- nondigestable BOOLEAN,
- nonmember_rejection_notice TEXT,
- obscure_addresses BOOLEAN,
- owner_chain TEXT,
- owner_pipeline TEXT,
- personalize INTEGER,
- post_id INTEGER,
- posting_chain TEXT,
- posting_pipeline TEXT,
- preferred_language TEXT,
- display_name TEXT,
- reject_these_nonmembers BLOB,
- reply_goes_to_list INTEGER,
- reply_to_address TEXT,
- require_explicit_destination BOOLEAN,
- respond_to_post_requests BOOLEAN,
- scrub_nondigest BOOLEAN,
- send_goodbye_message BOOLEAN,
- send_welcome_message BOOLEAN,
- subject_prefix TEXT,
- topics BLOB,
- topics_bodylines_limit INTEGER,
- topics_enabled BOOLEAN,
- welcome_message_uri TEXT,
- archive_policy INTEGER,
- list_id TEXT,
- nntp_prefix_subject_too INTEGER,
- newsgroup_moderation INTEGER,
- PRIMARY KEY (id)
- );
-
-INSERT INTO mailinglist_backup SELECT
- id,
- list_name,
- mail_host,
- allow_list_posts,
- include_rfc2369_headers,
- created_at,
- next_request_id,
- next_digest_number,
- digest_last_sent_at,
- volume,
- last_post_at,
- accept_these_nonmembers,
- acceptable_aliases_id,
- admin_immed_notify,
- admin_notify_mchanges,
- administrivia,
- advertised,
- anonymous_list,
- autorespond_owner,
- autoresponse_owner_text,
- autorespond_postings,
- autoresponse_postings_text,
- autorespond_requests,
- autoresponse_request_text,
- autoresponse_grace_period,
- forward_unrecognized_bounces_to,
- process_bounces,
- bounce_info_stale_after,
- bounce_matching_headers,
- bounce_notify_owner_on_disable,
- bounce_notify_owner_on_removal,
- bounce_score_threshold,
- bounce_you_are_disabled_warnings,
- bounce_you_are_disabled_warnings_interval,
- filter_action,
- filter_content,
- collapse_alternatives,
- convert_html_to_plaintext,
- default_member_action,
- default_nonmember_action,
- description,
- digest_footer_uri,
- digest_header_uri,
- digest_is_default,
- digest_send_periodic,
- digest_size_threshold,
- digest_volume_frequency,
- digestable,
- discard_these_nonmembers,
- emergency,
- encode_ascii_prefixes,
- first_strip_reply_to,
- footer_uri,
- forward_auto_discards,
- gateway_to_mail,
- gateway_to_news,
- goodbye_message_uri,
- header_matches,
- header_uri,
- hold_these_nonmembers,
- info,
- linked_newsgroup,
- max_days_to_hold,
- max_message_size,
- max_num_recipients,
- member_moderation_notice,
- mime_is_default_digest,
- moderator_password,
- nondigestable,
- nonmember_rejection_notice,
- obscure_addresses,
- owner_chain,
- owner_pipeline,
- personalize,
- post_id,
- posting_chain,
- posting_pipeline,
- preferred_language,
- display_name,
- reject_these_nonmembers,
- reply_goes_to_list,
- reply_to_address,
- require_explicit_destination,
- respond_to_post_requests,
- scrub_nondigest,
- send_goodbye_message,
- send_welcome_message,
- subject_prefix,
- topics,
- topics_bodylines_limit,
- topics_enabled,
- welcome_message_uri,
- archive_policy,
- list_id,
- nntp_prefix_subject_too,
- newsgroup_moderation
- FROM mailinglist;
diff --git a/src/mailman/database/schema/sqlite_20130406000000_01.sql b/src/mailman/database/schema/sqlite_20130406000000_01.sql
deleted file mode 100644
index fe30ed247..000000000
--- a/src/mailman/database/schema/sqlite_20130406000000_01.sql
+++ /dev/null
@@ -1,46 +0,0 @@
--- This file contains the SQLite schema migration from
--- 3.0b3 to 3.0b4
---
--- After 3.0b4 is released you may not edit this file.
-
--- For SQLite3 migration strategy, see
--- http://sqlite.org/faq.html#q11
-
--- ADD listarchiver table.
-
--- REMOVALs from the bounceevent table:
--- REM list_name
-
--- ADDs to the bounceevent table:
--- ADD list_id
-
--- ADDs to the mailinglist table:
--- ADD archiver_id
-
-CREATE TABLE bounceevent_backup (
- id INTEGER NOT NULL,
- email TEXT,
- 'timestamp' TIMESTAMP,
- message_id TEXT,
- context INTEGER,
- processed BOOLEAN,
- PRIMARY KEY (id)
- );
-
-INSERT INTO bounceevent_backup SELECT
- id, email, "timestamp", message_id,
- context, processed
- FROM bounceevent;
-
-ALTER TABLE bounceevent_backup ADD COLUMN list_id TEXT;
-
-CREATE TABLE listarchiver (
- id INTEGER NOT NULL,
- mailing_list_id INTEGER NOT NULL,
- name TEXT NOT NULL,
- _is_enabled BOOLEAN,
- PRIMARY KEY (id)
- );
-
-CREATE INDEX ix_listarchiver_mailing_list_id
- ON listarchiver(mailing_list_id);
diff --git a/src/mailman/database/sqlite.py b/src/mailman/database/sqlite.py
index 0594d9091..b70e474cc 100644
--- a/src/mailman/database/sqlite.py
+++ b/src/mailman/database/sqlite.py
@@ -56,7 +56,7 @@ class SQLiteDatabase(SABaseDatabase):
assert parts.scheme == 'sqlite', (
'Database url mismatch (expected sqlite prefix): {0}'.format(url))
path = os.path.normpath(parts.path)
- fd = os.open(path, os.O_WRONLY | os.O_NONBLOCK | os.O_CREAT, 0666)
+ fd = os.open(path, os.O_WRONLY | os.O_NONBLOCK | os.O_CREAT, 0o666)
# Ignore errors
if fd > 0:
os.close(fd)
@@ -72,7 +72,7 @@ def _cleanup(self, tempdir):
def make_temporary(database):
"""Adapts by monkey patching an existing SQLite IDatabase."""
tempdir = tempfile.mkdtemp()
- url = 'sqlite:///' + os.path.join(tempdir, 'mailman.db')
+ url = 'sqlite:///' + os.path.join(tempdir, 'mailman.db')
with configuration('database', url=url):
database.initialize()
database._cleanup = types.MethodType(
diff --git a/src/mailman/database/tests/__init__.py b/src/mailman/database/tests/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/src/mailman/database/tests/__init__.py
+++ /dev/null
diff --git a/src/mailman/database/tests/data/__init__.py b/src/mailman/database/tests/data/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/src/mailman/database/tests/data/__init__.py
+++ /dev/null
diff --git a/src/mailman/database/tests/data/mailman_01.db b/src/mailman/database/tests/data/mailman_01.db
deleted file mode 100644
index 1ff8d8343..000000000
--- a/src/mailman/database/tests/data/mailman_01.db
+++ /dev/null
Binary files differ
diff --git a/src/mailman/database/tests/data/migration_postgres_1.sql b/src/mailman/database/tests/data/migration_postgres_1.sql
deleted file mode 100644
index b82ecf6e4..000000000
--- a/src/mailman/database/tests/data/migration_postgres_1.sql
+++ /dev/null
@@ -1,133 +0,0 @@
-INSERT INTO "acceptablealias" VALUES(1,'foo@example.com',1);
-INSERT INTO "acceptablealias" VALUES(2,'bar@example.com',1);
-
-INSERT INTO "address" VALUES(
- 1,'anne@example.com',NULL,'Anne Person',
- '2012-04-19 00:52:24.826432','2012-04-19 00:49:42.373769',1,2);
-INSERT INTO "address" VALUES(
- 2,'bart@example.com',NULL,'Bart Person',
- '2012-04-19 00:53:25.878800','2012-04-19 00:49:52.882050',2,4);
-
-INSERT INTO "domain" VALUES(
- 1,'example.com','http://example.com',NULL,'postmaster@example.com');
-
-INSERT INTO "mailinglist" VALUES(
- -- id,list_name,mail_host,include_list_post_header,include_rfc2369_headers
- 1,'test','example.com',True,True,
- -- created_at,admin_member_chunksize,next_request_id,next_digest_number
- '2012-04-19 00:46:13.173844',30,1,1,
- -- digest_last_sent_at,volume,last_post_at,accept_these_nonmembers
- NULL,1,NULL,E'\\x80025D71012E',
- -- acceptable_aliases_id,admin_immed_notify,admin_notify_mchanges
- NULL,True,False,
- -- administrivia,advertised,anonymous_list,archive,archive_private
- True,True,False,True,False,
- -- archive_volume_frequency
- 1,
- --autorespond_owner,autoresponse_owner_text
- 0,'',
- -- autorespond_postings,autoresponse_postings_text
- 0,'',
- -- autorespond_requests,authoresponse_requests_text
- 0,'',
- -- autoresponse_grace_period
- '90 days, 0:00:00',
- -- forward_unrecognized_bounces_to,process_bounces
- 1,True,
- -- bounce_info_stale_after,bounce_matching_headers
- '7 days, 0:00:00','
-# Lines that *start* with a ''#'' are comments.
-to: friend@public.com
-message-id: relay.comanche.denmark.eu
-from: list@listme.com
-from: .*@uplinkpro.com
-',
- -- bounce_notify_owner_on_disable,bounce_notify_owner_on_removal
- True,True,
- -- bounce_score_threshold,bounce_you_are_disabled_warnings
- 5,3,
- -- bounce_you_are_disabled_warnings_interval
- '7 days, 0:00:00',
- -- filter_action,filter_content,collapse_alternatives
- 2,False,True,
- -- convert_html_to_plaintext,default_member_action,default_nonmember_action
- False,4,0,
- -- description
- '',
- -- digest_footer_uri
- 'mailman:///$listname/$language/footer-generic.txt',
- -- digest_header_uri
- NULL,
- -- digest_is_default,digest_send_periodic,digest_size_threshold
- False,True,30.0,
- -- digest_volume_frequency,digestable,discard_these_nonmembers
- 1,True,E'\\x80025D71012E',
- -- emergency,encode_ascii_prefixes,first_strip_reply_to
- False,False,False,
- -- footer_uri
- 'mailman:///$listname/$language/footer-generic.txt',
- -- forward_auto_discards,gateway_to_mail,gateway_to_news
- True,False,FAlse,
- -- generic_nonmember_action,goodby_message_uri
- 1,'',
- -- header_matches,header_uri,hold_these_nonmembers,info,linked_newsgroup
- E'\\x80025D71012E',NULL,E'\\x80025D71012E','','',
- -- max_days_to_hold,max_message_size,max_num_recipients
- 0,40,10,
- -- member_moderation_notice,mime_is_default_digest,moderator_password
- '',False,NULL,
- -- new_member_options,news_moderation,news_prefix_subject_too
- 256,0,True,
- -- nntp_host,nondigestable,nonmember_rejection_notice,obscure_addresses
- '',True,'',True,
- -- owner_chain,owner_pipeline,personalize,post_id
- 'default-owner-chain','default-owner-pipeline',0,1,
- -- posting_chain,posting_pipeline,preferred_language,private_roster
- 'default-posting-chain','default-posting-pipeline','en',True,
- -- display_name,reject_these_nonmembers
- 'Test',E'\\x80025D71012E',
- -- reply_goes_to_list,reply_to_address
- 0,'',
- -- require_explicit_destination,respond_to_post_requests
- True,True,
- -- scrub_nondigest,send_goodbye_message,send_reminders,send_welcome_message
- False,True,True,True,
- -- subject_prefix,subscribe_auto_approval
- '[Test] ',E'\\x80025D71012E',
- -- subscribe_policy,topics,topics_bodylines_limit,topics_enabled
- 1,E'\\x80025D71012E',5,False,
- -- unsubscribe_policy,welcome_message_uri
- 0,'mailman:///welcome.txt');
-
-INSERT INTO "member" VALUES(
- 1,'d1243f4d-e604-4f6b-af52-98d0a7bce0f1',1,'test@example.com',4,NULL,5,1);
-INSERT INTO "member" VALUES(
- 2,'dccc3851-fdfb-4afa-90cf-bdcbf80ad0fd',2,'test@example.com',3,NULL,6,1);
-INSERT INTO "member" VALUES(
- 3,'479be431-45f2-473d-bc3c-7eac614030ac',3,'test@example.com',3,NULL,7,2);
-INSERT INTO "member" VALUES(
- 4,'e2dc604c-d93a-4b91-b5a8-749e3caade36',1,'test@example.com',4,NULL,8,2);
-
-INSERT INTO "preferences" VALUES(1,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(2,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(3,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(4,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(5,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(6,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(7,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(8,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-
-INSERT INTO "user" VALUES(
- 1,'Anne Person',NULL,'0adf3caa-6f26-46f8-a11d-5256c8148592',
- '2012-04-19 00:49:42.370493',1,1);
-INSERT INTO "user" VALUES(
- 2,'Bart Person',NULL,'63f5d1a2-e533-4055-afe4-475dec3b1163',
- '2012-04-19 00:49:52.868746',2,3);
-
-INSERT INTO "uid" VALUES(1,'8bf9a615-f23e-4980-b7d1-90ac0203c66f');
-INSERT INTO "uid" VALUES(2,'0adf3caa-6f26-46f8-a11d-5256c8148592');
-INSERT INTO "uid" VALUES(3,'63f5d1a2-e533-4055-afe4-475dec3b1163');
-INSERT INTO "uid" VALUES(4,'d1243f4d-e604-4f6b-af52-98d0a7bce0f1');
-INSERT INTO "uid" VALUES(5,'dccc3851-fdfb-4afa-90cf-bdcbf80ad0fd');
-INSERT INTO "uid" VALUES(6,'479be431-45f2-473d-bc3c-7eac614030ac');
-INSERT INTO "uid" VALUES(7,'e2dc604c-d93a-4b91-b5a8-749e3caade36');
diff --git a/src/mailman/database/tests/data/migration_sqlite_1.sql b/src/mailman/database/tests/data/migration_sqlite_1.sql
deleted file mode 100644
index a5ac96dfa..000000000
--- a/src/mailman/database/tests/data/migration_sqlite_1.sql
+++ /dev/null
@@ -1,133 +0,0 @@
-INSERT INTO "acceptablealias" VALUES(1,'foo@example.com',1);
-INSERT INTO "acceptablealias" VALUES(2,'bar@example.com',1);
-
-INSERT INTO "address" VALUES(
- 1,'anne@example.com',NULL,'Anne Person',
- '2012-04-19 00:52:24.826432','2012-04-19 00:49:42.373769',1,2);
-INSERT INTO "address" VALUES(
- 2,'bart@example.com',NULL,'Bart Person',
- '2012-04-19 00:53:25.878800','2012-04-19 00:49:52.882050',2,4);
-
-INSERT INTO "domain" VALUES(
- 1,'example.com','http://example.com',NULL,'postmaster@example.com');
-
-INSERT INTO "mailinglist" VALUES(
- -- id,list_name,mail_host,include_list_post_header,include_rfc2369_headers
- 1,'test','example.com',1,1,
- -- created_at,admin_member_chunksize,next_request_id,next_digest_number
- '2012-04-19 00:46:13.173844',30,1,1,
- -- digest_last_sent_at,volume,last_post_at,accept_these_nonmembers
- NULL,1,NULL,X'80025D71012E',
- -- acceptable_aliases_id,admin_immed_notify,admin_notify_mchanges
- NULL,1,0,
- -- administrivia,advertised,anonymous_list,archive,archive_private
- 1,1,0,1,0,
- -- archive_volume_frequency
- 1,
- --autorespond_owner,autoresponse_owner_text
- 0,'',
- -- autorespond_postings,autoresponse_postings_text
- 0,'',
- -- autorespond_requests,authoresponse_requests_text
- 0,'',
- -- autoresponse_grace_period
- '90 days, 0:00:00',
- -- forward_unrecognized_bounces_to,process_bounces
- 1,1,
- -- bounce_info_stale_after,bounce_matching_headers
- '7 days, 0:00:00','
-# Lines that *start* with a ''#'' are comments.
-to: friend@public.com
-message-id: relay.comanche.denmark.eu
-from: list@listme.com
-from: .*@uplinkpro.com
-',
- -- bounce_notify_owner_on_disable,bounce_notify_owner_on_removal
- 1,1,
- -- bounce_score_threshold,bounce_you_are_disabled_warnings
- 5,3,
- -- bounce_you_are_disabled_warnings_interval
- '7 days, 0:00:00',
- -- filter_action,filter_content,collapse_alternatives
- 2,0,1,
- -- convert_html_to_plaintext,default_member_action,default_nonmember_action
- 0,4,0,
- -- description
- '',
- -- digest_footer_uri
- 'mailman:///$listname/$language/footer-generic.txt',
- -- digest_header_uri
- NULL,
- -- digest_is_default,digest_send_periodic,digest_size_threshold
- 0,1,30.0,
- -- digest_volume_frequency,digestable,discard_these_nonmembers
- 1,1,X'80025D71012E',
- -- emergency,encode_ascii_prefixes,first_strip_reply_to
- 0,0,0,
- -- footer_uri
- 'mailman:///$listname/$language/footer-generic.txt',
- -- forward_auto_discards,gateway_to_mail,gateway_to_news
- 1,0,0,
- -- generic_nonmember_action,goodby_message_uri
- 1,'',
- -- header_matches,header_uri,hold_these_nonmembers,info,linked_newsgroup
- X'80025D71012E',NULL,X'80025D71012E','','',
- -- max_days_to_hold,max_message_size,max_num_recipients
- 0,40,10,
- -- member_moderation_notice,mime_is_default_digest,moderator_password
- '',0,NULL,
- -- new_member_options,news_moderation,news_prefix_subject_too
- 256,0,1,
- -- nntp_host,nondigestable,nonmember_rejection_notice,obscure_addresses
- '',1,'',1,
- -- owner_chain,owner_pipeline,personalize,post_id
- 'default-owner-chain','default-owner-pipeline',0,1,
- -- posting_chain,posting_pipeline,preferred_language,private_roster
- 'default-posting-chain','default-posting-pipeline','en',1,
- -- display_name,reject_these_nonmembers
- 'Test',X'80025D71012E',
- -- reply_goes_to_list,reply_to_address
- 0,'',
- -- require_explicit_destination,respond_to_post_requests
- 1,1,
- -- scrub_nondigest,send_goodbye_message,send_reminders,send_welcome_message
- 0,1,1,1,
- -- subject_prefix,subscribe_auto_approval
- '[Test] ',X'80025D71012E',
- -- subscribe_policy,topics,topics_bodylines_limit,topics_enabled
- 1,X'80025D71012E',5,0,
- -- unsubscribe_policy,welcome_message_uri
- 0,'mailman:///welcome.txt');
-
-INSERT INTO "member" VALUES(
- 1,'d1243f4d-e604-4f6b-af52-98d0a7bce0f1',1,'test@example.com',4,NULL,5,1);
-INSERT INTO "member" VALUES(
- 2,'dccc3851-fdfb-4afa-90cf-bdcbf80ad0fd',2,'test@example.com',3,NULL,6,1);
-INSERT INTO "member" VALUES(
- 3,'479be431-45f2-473d-bc3c-7eac614030ac',3,'test@example.com',3,NULL,7,2);
-INSERT INTO "member" VALUES(
- 4,'e2dc604c-d93a-4b91-b5a8-749e3caade36',1,'test@example.com',4,NULL,8,2);
-
-INSERT INTO "preferences" VALUES(1,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(2,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(3,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(4,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(5,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(6,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(7,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-INSERT INTO "preferences" VALUES(8,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
-
-INSERT INTO "user" VALUES(
- 1,'Anne Person',NULL,'0adf3caa-6f26-46f8-a11d-5256c8148592',
- '2012-04-19 00:49:42.370493',1,1);
-INSERT INTO "user" VALUES(
- 2,'Bart Person',NULL,'63f5d1a2-e533-4055-afe4-475dec3b1163',
- '2012-04-19 00:49:52.868746',2,3);
-
-INSERT INTO "uid" VALUES(1,'8bf9a615-f23e-4980-b7d1-90ac0203c66f');
-INSERT INTO "uid" VALUES(2,'0adf3caa-6f26-46f8-a11d-5256c8148592');
-INSERT INTO "uid" VALUES(3,'63f5d1a2-e533-4055-afe4-475dec3b1163');
-INSERT INTO "uid" VALUES(4,'d1243f4d-e604-4f6b-af52-98d0a7bce0f1');
-INSERT INTO "uid" VALUES(5,'dccc3851-fdfb-4afa-90cf-bdcbf80ad0fd');
-INSERT INTO "uid" VALUES(6,'479be431-45f2-473d-bc3c-7eac614030ac');
-INSERT INTO "uid" VALUES(7,'e2dc604c-d93a-4b91-b5a8-749e3caade36');
diff --git a/src/mailman/database/tests/test_migrations.py b/src/mailman/database/tests/test_migrations.py
deleted file mode 100644
index e82674747..000000000
--- a/src/mailman/database/tests/test_migrations.py
+++ /dev/null
@@ -1,513 +0,0 @@
-# Copyright (C) 2012-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Test schema migrations."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'TestMigration20120407MigratedData',
- 'TestMigration20120407Schema',
- 'TestMigration20120407UnchangedData',
- 'TestMigration20121015MigratedData',
- 'TestMigration20121015Schema',
- 'TestMigration20130406MigratedData',
- 'TestMigration20130406Schema',
- ]
-
-
-import unittest
-
-from datetime import datetime
-from operator import attrgetter
-from pkg_resources import resource_string
-from sqlite3 import OperationalError
-from zope.component import getUtility
-
-from mailman.interfaces.database import IDatabaseFactory
-from mailman.interfaces.domain import IDomainManager
-from mailman.interfaces.archiver import ArchivePolicy
-from mailman.interfaces.bounce import BounceContext
-from mailman.interfaces.listmanager import IListManager
-from mailman.interfaces.mailinglist import IAcceptableAliasSet
-from mailman.interfaces.nntp import NewsgroupModeration
-from mailman.interfaces.subscriptions import ISubscriptionService
-from mailman.model.bans import Ban
-from mailman.model.bounce import BounceEvent
-from mailman.testing.helpers import temporary_db
-from mailman.testing.layers import ConfigLayer
-
-
-
-@unittest.skip('Migration tests are skipped')
-class MigrationTestBase(unittest.TestCase):
- """Test database migrations."""
-
- layer = ConfigLayer
-
- def setUp(self):
- self._database = getUtility(IDatabaseFactory, 'temporary').create()
-
- def tearDown(self):
- self._database._cleanup()
-
- def _table_missing_present(self, migrations, missing, present):
- """The appropriate migrations leave some tables missing and present.
-
- :param migrations: Sequence of migrations to load.
- :param missing: Tables which should be missing.
- :param present: Tables which should be present.
- """
- for migration in migrations:
- self._database.load_migrations(migration)
- self._database.store.commit()
- for table in missing:
- self.assertRaises(OperationalError,
- self._database.store.execute,
- 'select * from {};'.format(table))
- for table in present:
- self._database.store.execute('select * from {};'.format(table))
-
- def _missing_present(self, table, migrations, missing, present):
- """The appropriate migrations leave columns missing and present.
-
- :param table: The table to test columns from.
- :param migrations: Sequence of migrations to load.
- :param missing: Set of columns which should be missing after the
- migrations are loaded.
- :param present: Set of columns which should be present after the
- migrations are loaded.
- """
- for migration in migrations:
- self._database.load_migrations(migration)
- self._database.store.commit()
- for column in missing:
- self.assertRaises(DatabaseError,
- self._database.store.execute,
- 'select {0} from {1};'.format(column, table))
- self._database.store.rollback()
- for column in present:
- # This should not produce an exception. Is there some better test
- # that we can perform?
- self._database.store.execute(
- 'select {0} from {1};'.format(column, table))
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20120407Schema(MigrationTestBase):
- """Test column migrations."""
-
- def test_pre_upgrade_columns_migration(self):
- # Test that before the migration, the old table columns are present
- # and the new database columns are not.
- self._missing_present('mailinglist',
- ['20120406999999'],
- # New columns are missing.
- ('allow_list_posts',
- 'archive_policy',
- 'list_id',
- 'nntp_prefix_subject_too'),
- # Old columns are present.
- ('archive',
- 'archive_private',
- 'archive_volume_frequency',
- 'generic_nonmember_action',
- 'include_list_post_header',
- 'news_moderation',
- 'news_prefix_subject_too',
- 'nntp_host'))
- self._missing_present('member',
- ['20120406999999'],
- ('list_id',),
- ('mailing_list',))
-
- def test_post_upgrade_columns_migration(self):
- # Test that after the migration, the old table columns are missing
- # and the new database columns are present.
- self._missing_present('mailinglist',
- ['20120406999999',
- '20120407000000'],
- # The old columns are missing.
- ('archive',
- 'archive_private',
- 'archive_volume_frequency',
- 'generic_nonmember_action',
- 'include_list_post_header',
- 'news_moderation',
- 'news_prefix_subject_too',
- 'nntp_host'),
- # The new columns are present.
- ('allow_list_posts',
- 'archive_policy',
- 'list_id',
- 'nntp_prefix_subject_too'))
- self._missing_present('member',
- ['20120406999999',
- '20120407000000'],
- ('mailing_list',),
- ('list_id',))
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20120407UnchangedData(MigrationTestBase):
- """Test non-migrated data."""
-
- def setUp(self):
- MigrationTestBase.setUp(self)
- # Load all the migrations to just before the one we're testing.
- self._database.load_migrations('20120406999999')
- # Load the previous schema's sample data.
- sample_data = resource_string(
- 'mailman.database.tests.data',
- 'migration_{0}_1.sql'.format(self._database.TAG))
- self._database.load_sql(self._database.store, sample_data)
- # XXX 2012-12-28: We have to load the last migration defined in the
- # system, otherwise the ORM model will not match the SQL table
- # definitions and we'll get OperationalErrors from SQLite.
- self._database.load_migrations('20121015000000')
-
- def test_migration_domains(self):
- # Test that the domains table, which isn't touched, doesn't change.
- with temporary_db(self._database):
- # Check that the domains survived the migration. This table
- # was not touched so it should be fine.
- domains = list(getUtility(IDomainManager))
- self.assertEqual(len(domains), 1)
- self.assertEqual(domains[0].mail_host, 'example.com')
-
- def test_migration_mailing_lists(self):
- # Test that the mailing lists survive migration.
- with temporary_db(self._database):
- # There should be exactly one mailing list defined.
- mlists = list(getUtility(IListManager).mailing_lists)
- self.assertEqual(len(mlists), 1)
- self.assertEqual(mlists[0].fqdn_listname, 'test@example.com')
-
- def test_migration_acceptable_aliases(self):
- # Test that the mailing list's acceptable aliases survive migration.
- # This proves that foreign key references are migrated properly.
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- aliases_set = IAcceptableAliasSet(mlist)
- self.assertEqual(set(aliases_set.aliases),
- set(['foo@example.com', 'bar@example.com']))
-
- def test_migration_members(self):
- # Test that the members of a mailing list all survive migration.
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- # Test that all the members we expect are still there. Start with
- # the two list delivery members.
- addresses = set(address.email
- for address in mlist.members.addresses)
- self.assertEqual(addresses,
- set(['anne@example.com', 'bart@example.com']))
- # There is one owner.
- owners = set(address.email for address in mlist.owners.addresses)
- self.assertEqual(len(owners), 1)
- self.assertEqual(owners.pop(), 'anne@example.com')
- # There is one moderator.
- moderators = set(address.email
- for address in mlist.moderators.addresses)
- self.assertEqual(len(moderators), 1)
- self.assertEqual(moderators.pop(), 'bart@example.com')
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20120407MigratedData(MigrationTestBase):
- """Test affected migration data."""
-
- def setUp(self):
- MigrationTestBase.setUp(self)
- # Load all the migrations to just before the one we're testing.
- self._database.load_migrations('20120406999999')
- # Load the previous schema's sample data.
- sample_data = resource_string(
- 'mailman.database.tests.data',
- 'migration_{0}_1.sql'.format(self._database.TAG))
- self._database.load_sql(self._database.store, sample_data)
-
- def _upgrade(self):
- # XXX 2012-12-28: We have to load the last migration defined in the
- # system, otherwise the ORM model will not match the SQL table
- # definitions and we'll get OperationalErrors from SQLite.
- self._database.load_migrations('20121015000000')
-
- def test_migration_archive_policy_never_0(self):
- # Test that the new archive_policy value is updated correctly. In the
- # case of old column archive=0, the archive_private column is
- # ignored. This test sets it to 0 to ensure it's ignored.
- self._database.store.execute(
- 'UPDATE mailinglist SET archive = {0}, archive_private = {0} '
- 'WHERE id = 1;'.format(self._database.FALSE))
- # Complete the migration
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.archive_policy, ArchivePolicy.never)
-
- def test_migration_archive_policy_never_1(self):
- # Test that the new archive_policy value is updated correctly. In the
- # case of old column archive=0, the archive_private column is
- # ignored. This test sets it to 1 to ensure it's ignored.
- self._database.store.execute(
- 'UPDATE mailinglist SET archive = {0}, archive_private = {1} '
- 'WHERE id = 1;'.format(self._database.FALSE,
- self._database.TRUE))
- # Complete the migration
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.archive_policy, ArchivePolicy.never)
-
- def test_archive_policy_private(self):
- # Test that the new archive_policy value is updated correctly for
- # private archives.
- self._database.store.execute(
- 'UPDATE mailinglist SET archive = {0}, archive_private = {0} '
- 'WHERE id = 1;'.format(self._database.TRUE))
- # Complete the migration
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.archive_policy, ArchivePolicy.private)
-
- def test_archive_policy_public(self):
- # Test that the new archive_policy value is updated correctly for
- # public archives.
- self._database.store.execute(
- 'UPDATE mailinglist SET archive = {1}, archive_private = {0} '
- 'WHERE id = 1;'.format(self._database.FALSE,
- self._database.TRUE))
- # Complete the migration
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.archive_policy, ArchivePolicy.public)
-
- def test_list_id(self):
- # Test that the mailinglist table gets a list_id column.
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.list_id, 'test.example.com')
-
- def test_list_id_member(self):
- # Test that the member table's mailing_list column becomes list_id.
- self._upgrade()
- with temporary_db(self._database):
- service = getUtility(ISubscriptionService)
- members = list(service.find_members(list_id='test.example.com'))
- self.assertEqual(len(members), 4)
-
- def test_news_moderation_none(self):
- # Test that news_moderation becomes newsgroup_moderation.
- self._database.store.execute(
- 'UPDATE mailinglist SET news_moderation = 0 '
- 'WHERE id = 1;')
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.newsgroup_moderation,
- NewsgroupModeration.none)
-
- def test_news_moderation_open_moderated(self):
- # Test that news_moderation becomes newsgroup_moderation.
- self._database.store.execute(
- 'UPDATE mailinglist SET news_moderation = 1 '
- 'WHERE id = 1;')
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.newsgroup_moderation,
- NewsgroupModeration.open_moderated)
-
- def test_news_moderation_moderated(self):
- # Test that news_moderation becomes newsgroup_moderation.
- self._database.store.execute(
- 'UPDATE mailinglist SET news_moderation = 2 '
- 'WHERE id = 1;')
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertEqual(mlist.newsgroup_moderation,
- NewsgroupModeration.moderated)
-
- def test_nntp_prefix_subject_too_false(self):
- # Test that news_prefix_subject_too becomes nntp_prefix_subject_too.
- self._database.store.execute(
- 'UPDATE mailinglist SET news_prefix_subject_too = {0} '
- 'WHERE id = 1;'.format(self._database.FALSE))
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertFalse(mlist.nntp_prefix_subject_too)
-
- def test_nntp_prefix_subject_too_true(self):
- # Test that news_prefix_subject_too becomes nntp_prefix_subject_too.
- self._database.store.execute(
- 'UPDATE mailinglist SET news_prefix_subject_too = {0} '
- 'WHERE id = 1;'.format(self._database.TRUE))
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertTrue(mlist.nntp_prefix_subject_too)
-
- def test_allow_list_posts_false(self):
- # Test that include_list_post_header -> allow_list_posts.
- self._database.store.execute(
- 'UPDATE mailinglist SET include_list_post_header = {0} '
- 'WHERE id = 1;'.format(self._database.FALSE))
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertFalse(mlist.allow_list_posts)
-
- def test_allow_list_posts_true(self):
- # Test that include_list_post_header -> allow_list_posts.
- self._database.store.execute(
- 'UPDATE mailinglist SET include_list_post_header = {0} '
- 'WHERE id = 1;'.format(self._database.TRUE))
- self._upgrade()
- with temporary_db(self._database):
- mlist = getUtility(IListManager).get('test@example.com')
- self.assertTrue(mlist.allow_list_posts)
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20121015Schema(MigrationTestBase):
- """Test column migrations."""
-
- def test_pre_upgrade_column_migrations(self):
- self._missing_present('ban',
- ['20121014999999'],
- ('list_id',),
- ('mailing_list',))
- self._missing_present('mailinglist',
- ['20121014999999'],
- (),
- ('new_member_options', 'send_reminders',
- 'subscribe_policy', 'unsubscribe_policy',
- 'subscribe_auto_approval', 'private_roster',
- 'admin_member_chunksize'),
- )
-
- def test_post_upgrade_column_migrations(self):
- self._missing_present('ban',
- ['20121014999999',
- '20121015000000'],
- ('mailing_list',),
- ('list_id',))
- self._missing_present('mailinglist',
- ['20121014999999',
- '20121015000000'],
- ('new_member_options', 'send_reminders',
- 'subscribe_policy', 'unsubscribe_policy',
- 'subscribe_auto_approval', 'private_roster',
- 'admin_member_chunksize'),
- ())
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20121015MigratedData(MigrationTestBase):
- """Test non-migrated data."""
-
- def test_migration_bans(self):
- # Load all the migrations to just before the one we're testing.
- self._database.load_migrations('20121014999999')
- # Insert a list-specific ban.
- self._database.store.execute("""
- INSERT INTO ban VALUES (
- 1, 'anne@example.com', 'test@example.com');
- """)
- # Insert a global ban.
- self._database.store.execute("""
- INSERT INTO ban VALUES (
- 2, 'bart@example.com', NULL);
- """)
- # Update to the current migration we're testing.
- self._database.load_migrations('20121015000000')
- # Now both the local and global bans should still be present.
- bans = sorted(self._database.store.find(Ban),
- key=attrgetter('email'))
- self.assertEqual(bans[0].email, 'anne@example.com')
- self.assertEqual(bans[0].list_id, 'test.example.com')
- self.assertEqual(bans[1].email, 'bart@example.com')
- self.assertEqual(bans[1].list_id, None)
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20130406Schema(MigrationTestBase):
- """Test column migrations."""
-
- def test_pre_upgrade_column_migrations(self):
- self._missing_present('bounceevent',
- ['20130405999999'],
- ('list_id',),
- ('list_name',))
-
- def test_post_upgrade_column_migrations(self):
- self._missing_present('bounceevent',
- ['20130405999999',
- '20130406000000'],
- ('list_name',),
- ('list_id',))
-
- def test_pre_listarchiver_table(self):
- self._table_missing_present(['20130405999999'], ('listarchiver',), ())
-
- def test_post_listarchiver_table(self):
- self._table_missing_present(['20130405999999',
- '20130406000000'],
- (),
- ('listarchiver',))
-
-
-
-@unittest.skip('Migration tests are skipped')
-class TestMigration20130406MigratedData(MigrationTestBase):
- """Test migrated data."""
-
- def test_migration_bounceevent(self):
- # Load all migrations to just before the one we're testing.
- self._database.load_migrations('20130405999999')
- # Insert a bounce event.
- self._database.store.execute("""
- INSERT INTO bounceevent VALUES (
- 1, 'test@example.com', 'anne@example.com',
- '2013-04-06 21:12:00', '<abc@example.com>',
- 1, 0);
- """)
- # Update to the current migration we're testing
- self._database.load_migrations('20130406000000')
- # The bounce event should exist, but with a list-id instead of a fqdn
- # list name.
- events = list(self._database.store.find(BounceEvent))
- self.assertEqual(len(events), 1)
- self.assertEqual(events[0].list_id, 'test.example.com')
- self.assertEqual(events[0].email, 'anne@example.com')
- self.assertEqual(events[0].timestamp, datetime(2013, 4, 6, 21, 12))
- self.assertEqual(events[0].message_id, '<abc@example.com>')
- self.assertEqual(events[0].context, BounceContext.normal)
- self.assertFalse(events[0].processed)
diff --git a/src/mailman/database/types.py b/src/mailman/database/types.py
index a6f0b32ca..380ce37dc 100644
--- a/src/mailman/database/types.py
+++ b/src/mailman/database/types.py
@@ -29,30 +29,28 @@ __all__ = [
import uuid
from sqlalchemy import Integer
-from sqlalchemy.types import TypeDecorator, BINARY, CHAR
from sqlalchemy.dialects import postgresql
+from sqlalchemy.types import TypeDecorator, BINARY, CHAR
class Enum(TypeDecorator):
- """
- Stores an integer-based Enum as an integer in the database, and converts it
- on-the-fly.
- """
+ """Handle Python 3.4 style enums.
+ Stores an integer-based Enum as an integer in the database, and
+ converts it on-the-fly.
+ """
impl = Integer
- def __init__(self, *args, **kw):
- self.enum = kw.pop("enum")
- TypeDecorator.__init__(self, *args, **kw)
+ def __init__(self, enum, *args, **kw):
+ self.enum = enum
+ super(Enum, self).__init__(*args, **kw)
def process_bind_param(self, value, dialect):
if value is None:
return None
-
return value.value
-
def process_result_value(self, value, dialect):
if value is None:
return None
@@ -61,29 +59,12 @@ class Enum(TypeDecorator):
class UUID(TypeDecorator):
- """
- Stores a UUID in the database natively when it can and falls back to
- a BINARY(16) or a CHAR(32) when it can't.
-
- ::
-
- from sqlalchemy_utils import UUIDType
- import uuid
-
- class User(Base):
- __tablename__ = 'user'
+ """Handle UUIds."""
- # Pass `binary=False` to fallback to CHAR instead of BINARY
- id = sa.Column(UUIDType(binary=False), primary_key=True)
- """
impl = BINARY(16)
-
python_type = uuid.UUID
def __init__(self, binary=True, native=True):
- """
- :param binary: Whether to use a BINARY(16) or CHAR(32) fallback.
- """
self.binary = binary
self.native = native
@@ -91,7 +72,6 @@ class UUID(TypeDecorator):
if dialect.name == 'postgresql' and self.native:
# Use the native UUID type.
return dialect.type_descriptor(postgresql.UUID())
-
else:
# Fallback to either a BINARY or a CHAR.
kind = self.impl if self.binary else CHAR(32)
@@ -102,29 +82,22 @@ class UUID(TypeDecorator):
if value and not isinstance(value, uuid.UUID):
try:
value = uuid.UUID(value)
-
except (TypeError, ValueError):
value = uuid.UUID(bytes=value)
-
return value
def process_bind_param(self, value, dialect):
if value is None:
return value
-
if not isinstance(value, uuid.UUID):
value = self._coerce(value)
-
if self.native and dialect.name == 'postgresql':
return str(value)
-
return value.bytes if self.binary else value.hex
def process_result_value(self, value, dialect):
if value is None:
return value
-
if self.native and dialect.name == 'postgresql':
return uuid.UUID(value)
-
return uuid.UUID(bytes=value) if self.binary else uuid.UUID(value)
diff --git a/src/mailman/interfaces/database.py b/src/mailman/interfaces/database.py
index d8fde2b93..c2997ba6b 100644
--- a/src/mailman/interfaces/database.py
+++ b/src/mailman/interfaces/database.py
@@ -61,7 +61,7 @@ class IDatabase(Interface):
"""Abort the current transaction."""
store = Attribute(
- """The underlying SQLAlchemy store on which you can do queries.""")
+ """The underlying database object on which you can do queries.""")
diff --git a/src/mailman/interfaces/messages.py b/src/mailman/interfaces/messages.py
index 4980a4f9d..7b99578c4 100644
--- a/src/mailman/interfaces/messages.py
+++ b/src/mailman/interfaces/messages.py
@@ -83,7 +83,7 @@ class IMessageStore(Interface):
def get_message_by_hash(message_id_hash):
"""Return the message with the matching X-Message-ID-Hash.
-
+
:param message_id_hash: The X-Message-ID-Hash header contents to
search for.
:returns: The message, or None if no matching message was found.
diff --git a/src/mailman/model/address.py b/src/mailman/model/address.py
index 7203a31a5..d078f28d5 100644
--- a/src/mailman/model/address.py
+++ b/src/mailman/model/address.py
@@ -26,8 +26,8 @@ __all__ = [
from email.utils import formataddr
-from sqlalchemy import (Column, Integer, String, Unicode,
- ForeignKey, DateTime)
+from sqlalchemy import (
+ Column, DateTime, ForeignKey, Integer, Unicode)
from sqlalchemy.orm import relationship, backref
from zope.component import getUtility
from zope.event import notify
@@ -56,10 +56,11 @@ class Address(Model):
user_id = Column(Integer, ForeignKey('user.id'))
preferences_id = Column(Integer, ForeignKey('preferences.id'))
- preferences = relationship('Preferences',
- backref=backref('Address', uselist=False))
+ preferences = relationship(
+ 'Preferences', backref=backref('Address', uselist=False))
def __init__(self, email, display_name):
+ super(Address, self).__init__()
getUtility(IEmailValidator).validate(email)
lower_case = email.lower()
self.email = lower_case
diff --git a/src/mailman/model/autorespond.py b/src/mailman/model/autorespond.py
index c3aff174a..c74434f7b 100644
--- a/src/mailman/model/autorespond.py
+++ b/src/mailman/model/autorespond.py
@@ -26,8 +26,7 @@ __all__ = [
]
-from sqlalchemy import (Column, Integer, String, Unicode,
- ForeignKey, Date)
+from sqlalchemy import Column, Date, ForeignKey, Integer
from sqlalchemy import desc
from sqlalchemy.orm import relationship
from zope.interface import implementer
@@ -55,7 +54,7 @@ class AutoResponseRecord(Model):
mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
mailing_list = relationship('MailingList')
- response_type = Column(Enum(enum=Response))
+ response_type = Column(Enum(Response))
date_sent = Column(Date)
def __init__(self, mailing_list, address, response_type):
@@ -77,10 +76,10 @@ class AutoResponseSet:
def todays_count(self, store, address, response_type):
"""See `IAutoResponseSet`."""
return store.query(AutoResponseRecord).filter_by(
- address = address,
- mailing_list = self._mailing_list,
- response_type = response_type,
- date_sent = today()).count()
+ address=address,
+ mailing_list=self._mailing_list,
+ response_type=response_type,
+ date_sent=today()).count()
@dbconnection
def response_sent(self, store, address, response_type):
@@ -93,8 +92,8 @@ class AutoResponseSet:
def last_response(self, store, address, response_type):
"""See `IAutoResponseSet`."""
results = store.query(AutoResponseRecord).filter_by(
- address = address,
- mailing_list = self._mailing_list,
- response_type = response_type
+ address=address,
+ mailing_list=self._mailing_list,
+ response_type=response_type
).order_by(desc(AutoResponseRecord.date_sent))
return (None if results.count() == 0 else results.first())
diff --git a/src/mailman/model/bans.py b/src/mailman/model/bans.py
index fbbecaebd..8678fc1e7 100644
--- a/src/mailman/model/bans.py
+++ b/src/mailman/model/bans.py
@@ -72,8 +72,8 @@ class BanManager:
@dbconnection
def unban(self, store, email):
"""See `IBanManager`."""
- ban = store.query(Ban).filter_by(email=email,
- list_id=self._list_id).first()
+ ban = store.query(Ban).filter_by(
+ email=email, list_id=self._list_id).first()
if ban is not None:
store.delete(ban)
diff --git a/src/mailman/model/bounce.py b/src/mailman/model/bounce.py
index 1165fee96..cd658052d 100644
--- a/src/mailman/model/bounce.py
+++ b/src/mailman/model/bounce.py
@@ -27,7 +27,7 @@ __all__ = [
-from sqlalchemy import Column, Integer, Unicode, DateTime, Boolean
+from sqlalchemy import Boolean, Column, DateTime, Integer, Unicode
from zope.interface import implementer
from mailman.database.model import Model
@@ -50,7 +50,7 @@ class BounceEvent(Model):
email = Column(Unicode)
timestamp = Column(DateTime)
message_id = Column(Unicode)
- context = Column(Enum(enum=BounceContext))
+ context = Column(Enum(BounceContext))
processed = Column(Boolean)
def __init__(self, list_id, email, msg, context=None):
@@ -85,5 +85,5 @@ class BounceProcessor:
@dbconnection
def unprocessed(self, store):
"""See `IBounceProcessor`."""
- for event in store.query(BounceEvent).filter_by(processed = False):
+ for event in store.query(BounceEvent).filter_by(processed=False):
yield event
diff --git a/src/mailman/model/digests.py b/src/mailman/model/digests.py
index 1b7140824..7bfd512b6 100644
--- a/src/mailman/model/digests.py
+++ b/src/mailman/model/digests.py
@@ -50,7 +50,7 @@ class OneLastDigest(Model):
address_id = Column(Integer, ForeignKey('address.id'))
address = relationship('Address')
- delivery_mode = Column(Enum(enum=DeliveryMode))
+ delivery_mode = Column(Enum(DeliveryMode))
def __init__(self, mailing_list, address, delivery_mode):
self.mailing_list = mailing_list
diff --git a/src/mailman/model/docs/messagestore.rst b/src/mailman/model/docs/messagestore.rst
index 4ddce7606..f2f2ca9d2 100644
--- a/src/mailman/model/docs/messagestore.rst
+++ b/src/mailman/model/docs/messagestore.rst
@@ -28,8 +28,9 @@ header, you will get an exception.
However, if the message has a ``Message-ID`` header, it can be stored.
>>> msg['Message-ID'] = '<87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp>'
- >>> message_store.add(msg)
- 'AGDWSNXXKCWEILKKNYTBOHRDQGOX3Y35'
+ >>> x_message_id_hash = message_store.add(msg)
+ >>> print(x_message_id_hash)
+ AGDWSNXXKCWEILKKNYTBOHRDQGOX3Y35
>>> print(msg.as_string())
Subject: An important message
Message-ID: <87myycy5eh.fsf@uwakimon.sk.tsukuba.ac.jp>
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index 585eccf3d..083e1cf51 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -26,8 +26,8 @@ __all__ = [
]
+from sqlalchemy import Column, Integer, Unicode
from urlparse import urljoin, urlparse
-from sqlalchemy import Column, Unicode, Integer
from zope.event import notify
from zope.interface import implementer
diff --git a/src/mailman/model/language.py b/src/mailman/model/language.py
index 7b611b6d8..15450c936 100644
--- a/src/mailman/model/language.py
+++ b/src/mailman/model/language.py
@@ -25,8 +25,8 @@ __all__ = [
]
+from sqlalchemy import Column, Integer, Unicode
from zope.interface import implementer
-from sqlalchemy import Column, Unicode, Integer
from mailman.database import Model
from mailman.interfaces import ILanguage
diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py
index 385262f28..d00cf3d31 100644
--- a/src/mailman/model/mailinglist.py
+++ b/src/mailman/model/mailinglist.py
@@ -27,10 +27,11 @@ __all__ = [
import os
-from sqlalchemy import (Column, Boolean, DateTime, Float, Integer, Unicode,
- PickleType, Interval, ForeignKey, LargeBinary)
-from sqlalchemy import event
-from sqlalchemy.orm import relationship, sessionmaker
+from sqlalchemy import (
+ Boolean, Column, DateTime, Float, ForeignKey, Integer, Interval,
+ LargeBinary, PickleType, Unicode)
+from sqlalchemy.event import listen
+from sqlalchemy.orm import relationship
from urlparse import urljoin
from zope.component import getUtility
from zope.event import notify
@@ -38,6 +39,7 @@ from zope.interface import implementer
from mailman.config import config
from mailman.database.model import Model
+from mailman.database.transaction import dbconnection
from mailman.database.types import Enum
from mailman.interfaces.action import Action, FilterAction
from mailman.interfaces.address import IAddress
@@ -68,7 +70,6 @@ from mailman.utilities.string import expand
SPACE = ' '
UNDERSCORE = '_'
-Session = sessionmaker()
@implementer(IMailingList)
@@ -100,9 +101,6 @@ class MailingList(Model):
digest_last_sent_at = Column(DateTime)
volume = Column(Integer)
last_post_at = Column(DateTime)
- # Implicit destination.
- # acceptable_aliases_id = Column(Integer, ForeignKey('acceptablealias.id'))
- # acceptable_alias = relationship('AcceptableAlias', backref='mailing_list')
# 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.
@@ -110,17 +108,17 @@ class MailingList(Model):
admin_immed_notify = Column(Boolean)
admin_notify_mchanges = Column(Boolean)
administrivia = Column(Boolean)
- archive_policy = Column(Enum(enum=ArchivePolicy))
+ archive_policy = Column(Enum(ArchivePolicy))
# Automatic responses.
autoresponse_grace_period = Column(Interval)
- autorespond_owner = Column(Enum(enum=ResponseAction))
+ autorespond_owner = Column(Enum(ResponseAction))
autoresponse_owner_text = Column(Unicode)
- autorespond_postings = Column(Enum(enum=ResponseAction))
+ autorespond_postings = Column(Enum(ResponseAction))
autoresponse_postings_text = Column(Unicode)
- autorespond_requests = Column(Enum(enum=ResponseAction))
+ autorespond_requests = Column(Enum(ResponseAction))
autoresponse_request_text = Column(Unicode)
# Content filters.
- filter_action = Column(Enum(enum=FilterAction))
+ filter_action = Column(Enum(FilterAction))
filter_content = Column(Boolean)
collapse_alternatives = Column(Boolean)
convert_html_to_plaintext = Column(Boolean)
@@ -132,18 +130,19 @@ class MailingList(Model):
bounce_score_threshold = Column(Integer) # XXX
bounce_you_are_disabled_warnings = Column(Integer) # XXX
bounce_you_are_disabled_warnings_interval = Column(Interval) # XXX
- forward_unrecognized_bounces_to = Column(Enum(enum=UnrecognizedBounceDisposition))
+ forward_unrecognized_bounces_to = Column(
+ Enum(UnrecognizedBounceDisposition))
process_bounces = Column(Boolean)
# Miscellaneous
- default_member_action = Column(Enum(enum=Action))
- default_nonmember_action = Column(Enum(enum=Action))
+ default_member_action = Column(Enum(Action))
+ default_nonmember_action = Column(Enum(Action))
description = Column(Unicode)
digest_footer_uri = Column(Unicode)
digest_header_uri = Column(Unicode)
digest_is_default = Column(Boolean)
digest_send_periodic = Column(Boolean)
digest_size_threshold = Column(Float)
- digest_volume_frequency = Column(Enum(enum=DigestFrequency))
+ digest_volume_frequency = Column(Enum(DigestFrequency))
digestable = Column(Boolean)
discard_these_nonmembers = Column(PickleType)
emergency = Column(Boolean)
@@ -166,21 +165,21 @@ class MailingList(Model):
mime_is_default_digest = Column(Boolean)
# FIXME: There should be no moderator_password
moderator_password = Column(LargeBinary) # TODO : was RawStr()
- newsgroup_moderation = Column(Enum(enum=NewsgroupModeration))
+ newsgroup_moderation = Column(Enum(NewsgroupModeration))
nntp_prefix_subject_too = Column(Boolean)
nondigestable = Column(Boolean)
nonmember_rejection_notice = Column(Unicode)
obscure_addresses = Column(Boolean)
owner_chain = Column(Unicode)
owner_pipeline = Column(Unicode)
- personalize = Column(Enum(enum=Personalization))
+ personalize = Column(Enum(Personalization))
post_id = Column(Integer)
posting_chain = Column(Unicode)
posting_pipeline = Column(Unicode)
_preferred_language = Column('preferred_language', Unicode)
display_name = Column(Unicode)
reject_these_nonmembers = Column(PickleType)
- reply_goes_to_list = Column(Enum(enum=ReplyToMunging))
+ reply_goes_to_list = Column(Enum(ReplyToMunging))
reply_to_address = Column(Unicode)
require_explicit_destination = Column(Boolean)
respond_to_post_requests = Column(Boolean)
@@ -194,6 +193,7 @@ class MailingList(Model):
welcome_message_uri = Column(Unicode)
def __init__(self, fqdn_listname):
+ super(MailingList, self).__init__()
listname, at, hostname = fqdn_listname.partition('@')
assert hostname, 'Bad list name: {0}'.format(fqdn_listname)
self.list_name = listname
@@ -201,15 +201,15 @@ class MailingList(Model):
self._list_id = '{0}.{1}'.format(listname, hostname)
# For the pending database
self.next_request_id = 1
- # We need to set up the rosters. Normally, this method will get
- # called when the MailingList object is loaded from the database, but
- # that's not the case when the constructor is called. So, set up the
- # rosters explicitly.
+ # We need to set up the rosters. Normally, this method will get called
+ # when the MailingList object is loaded from the database, but when the
+ # constructor is called, SQLAlchemy's `load` event isn't triggered.
+ # Thus we need to set up the rosters explicitly.
self._post_load()
makedirs(self.data_path)
-
def _post_load(self, *args):
+ # This hooks up to SQLAlchemy's `load` event.
self.owners = roster.OwnerRoster(self)
self.moderators = roster.ModeratorRoster(self)
self.administrators = roster.AdministratorRoster(self)
@@ -221,7 +221,10 @@ class MailingList(Model):
@classmethod
def __declare_last__(cls):
- event.listen(cls, 'load', cls._post_load)
+ # SQLAlchemy special directive hook called after mappings are assumed
+ # to be complete. Use this to connect the roster instance creation
+ # method with the SA `load` event.
+ listen(cls, 'load', cls._post_load)
def __repr__(self):
return '<mailing list "{0}" at {1:#x}>'.format(
@@ -331,15 +334,17 @@ class MailingList(Model):
except AttributeError:
self._preferred_language = language
- def send_one_last_digest_to(self, address, delivery_mode):
+ @dbconnection
+ def send_one_last_digest_to(self, store, address, delivery_mode):
"""See `IMailingList`."""
digest = OneLastDigest(self, address, delivery_mode)
- Session.object_session(self).add(digest)
+ store.add(digest)
@property
- def last_digest_recipients(self):
+ @dbconnection
+ def last_digest_recipients(self, store):
"""See `IMailingList`."""
- results = Session.object_session(self).query(OneLastDigest).filter(
+ results = store.query(OneLastDigest).filter(
OneLastDigest.mailing_list == self)
recipients = [(digest.address, digest.delivery_mode)
for digest in results]
@@ -347,19 +352,20 @@ class MailingList(Model):
return recipients
@property
- def filter_types(self):
+ @dbconnection
+ def filter_types(self, store):
"""See `IMailingList`."""
- results = Session.object_session(self).query(ContentFilter).filter(
+ results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.filter_mime)
for content_filter in results:
yield content_filter.filter_pattern
@filter_types.setter
- def filter_types(self, sequence):
+ @dbconnection
+ def filter_types(self, store, sequence):
"""See `IMailingList`."""
# First, delete all existing MIME type filter patterns.
- store = Session.object_session(self)
results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.filter_mime)
@@ -371,19 +377,20 @@ class MailingList(Model):
store.add(content_filter)
@property
- def pass_types(self):
+ @dbconnection
+ def pass_types(self, store):
"""See `IMailingList`."""
- results = Session.object_session(self).query(ContentFilter).filter(
+ results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.pass_mime)
for content_filter in results:
yield content_filter.filter_pattern
@pass_types.setter
- def pass_types(self, sequence):
+ @dbconnection
+ def pass_types(self, store, sequence):
"""See `IMailingList`."""
# First, delete all existing MIME type pass patterns.
- store = Session.object_session(self)
results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.pass_mime)
@@ -395,19 +402,20 @@ class MailingList(Model):
store.add(content_filter)
@property
- def filter_extensions(self):
+ @dbconnection
+ def filter_extensions(self, store):
"""See `IMailingList`."""
- results = Session.object_session(self).query(ContentFilter).filter(
+ results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.filter_extension)
for content_filter in results:
yield content_filter.filter_pattern
@filter_extensions.setter
- def filter_extensions(self, sequence):
+ @dbconnection
+ def filter_extensions(self, store, sequence):
"""See `IMailingList`."""
# First, delete all existing file extensions filter patterns.
- store = Session.object_session(self)
results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.filter_extension)
@@ -419,19 +427,20 @@ class MailingList(Model):
store.add(content_filter)
@property
- def pass_extensions(self):
+ @dbconnection
+ def pass_extensions(self, store):
"""See `IMailingList`."""
- results = Session.object_session(self).query(ContentFilter).filter(
+ results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.pass_extension)
for content_filter in results:
yield content_filter.pass_pattern
@pass_extensions.setter
- def pass_extensions(self, sequence):
+ @dbconnection
+ def pass_extensions(self, store, sequence):
"""See `IMailingList`."""
# First, delete all existing file extensions pass patterns.
- store = Session.object_session(self)
results = store.query(ContentFilter).filter(
ContentFilter.mailing_list == self,
ContentFilter.filter_type == FilterType.pass_extension)
@@ -454,9 +463,9 @@ class MailingList(Model):
raise TypeError(
'Undefined MemberRole: {0}'.format(role))
- def subscribe(self, subscriber, role=MemberRole.member):
+ @dbconnection
+ def subscribe(self, store, subscriber, role=MemberRole.member):
"""See `IMailingList`."""
- store = Session.object_session(self)
if IAddress.providedBy(subscriber):
member = store.query(Member).filter(
Member.role == role,
@@ -512,29 +521,30 @@ class AcceptableAliasSet:
def __init__(self, mailing_list):
self._mailing_list = mailing_list
- def clear(self):
+ @dbconnection
+ def clear(self, store):
"""See `IAcceptableAliasSet`."""
- Session.object_session(self._mailing_list).query(
- AcceptableAlias).filter(
- AcceptableAlias.mailing_list == self._mailing_list).delete()
+ store.query(AcceptableAlias).filter(
+ AcceptableAlias.mailing_list == self._mailing_list).delete()
- def add(self, alias):
+ @dbconnection
+ def add(self, store, alias):
if not (alias.startswith('^') or '@' in alias):
raise ValueError(alias)
alias = AcceptableAlias(self._mailing_list, alias.lower())
- Session.object_session(self._mailing_list).add(alias)
+ store.add(alias)
- def remove(self, alias):
- Session.object_session(self._mailing_list).query(
- AcceptableAlias).filter(
- AcceptableAlias.mailing_list == self._mailing_list,
- AcceptableAlias.alias == alias.lower()).delete()
+ @dbconnection
+ def remove(self, store, alias):
+ store.query(AcceptableAlias).filter(
+ AcceptableAlias.mailing_list == self._mailing_list,
+ AcceptableAlias.alias == alias.lower()).delete()
@property
- def aliases(self):
- aliases = Session.object_session(self._mailing_list).query(
- AcceptableAlias).filter(
- AcceptableAlias.mailing_list_id == self._mailing_list.id)
+ @dbconnection
+ def aliases(self, store):
+ aliases = store.query(AcceptableAlias).filter(
+ AcceptableAlias.mailing_list_id == self._mailing_list.id)
for alias in aliases:
yield alias.alias
@@ -576,14 +586,14 @@ class ListArchiver(Model):
@implementer(IListArchiverSet)
class ListArchiverSet:
- def __init__(self, mailing_list):
+ @dbconnection
+ def __init__(self, store, mailing_list):
self._mailing_list = mailing_list
system_archivers = {}
for archiver in config.archivers:
system_archivers[archiver.name] = archiver
# Add any system enabled archivers which aren't already associated
# with the mailing list.
- store = Session.object_session(self._mailing_list)
for archiver_name in system_archivers:
exists = store.query(ListArchiver).filter(
ListArchiver.mailing_list == mailing_list,
@@ -593,14 +603,15 @@ class ListArchiverSet:
system_archivers[archiver_name]))
@property
- def archivers(self):
- entries = Session.object_session(self._mailing_list).query(
- ListArchiver).filter(ListArchiver.mailing_list == self._mailing_list)
+ @dbconnection
+ def archivers(self, store):
+ entries = store.query(ListArchiver).filter(
+ ListArchiver.mailing_list == self._mailing_list)
for entry in entries:
yield entry
- def get(self, archiver_name):
- return Session.object_session(self._mailing_list).query(
- ListArchiver).filter(
- ListArchiver.mailing_list == self._mailing_list,
- ListArchiver.name == archiver_name).first()
+ @dbconnection
+ def get(self, store, archiver_name):
+ return store.query(ListArchiver).filter(
+ ListArchiver.mailing_list == self._mailing_list,
+ ListArchiver.name == archiver_name).first()
diff --git a/src/mailman/model/member.py b/src/mailman/model/member.py
index f1007c311..9da9d5d0d 100644
--- a/src/mailman/model/member.py
+++ b/src/mailman/model/member.py
@@ -24,7 +24,7 @@ __all__ = [
'Member',
]
-from sqlalchemy import Integer, Unicode, ForeignKey, Column
+from sqlalchemy import Column, ForeignKey, Integer, Unicode
from sqlalchemy.orm import relationship
from zope.component import getUtility
from zope.event import notify
@@ -56,9 +56,9 @@ class Member(Model):
id = Column(Integer, primary_key=True)
_member_id = Column(UUID)
- role = Column(Enum(enum=MemberRole))
+ role = Column(Enum(MemberRole))
list_id = Column(Unicode)
- moderation_action = Column(Enum(enum=Action))
+ moderation_action = Column(Enum(Action))
address_id = Column(Integer, ForeignKey('address.id'))
_address = relationship('Address')
diff --git a/src/mailman/model/message.py b/src/mailman/model/message.py
index 39f33aa89..74a76ac30 100644
--- a/src/mailman/model/message.py
+++ b/src/mailman/model/message.py
@@ -24,7 +24,7 @@ __all__ = [
'Message',
]
-from sqlalchemy import Column, Integer, Unicode, LargeBinary
+from sqlalchemy import Column, Integer, LargeBinary, Unicode
from zope.interface import implementer
from mailman.database.model import Model
@@ -47,6 +47,7 @@ class Message(Model):
@dbconnection
def __init__(self, store, message_id, message_id_hash, path):
+ super(Message, self).__init__()
self.message_id = message_id
self.message_id_hash = message_id_hash
self.path = path
diff --git a/src/mailman/model/messagestore.py b/src/mailman/model/messagestore.py
index f9f224dd6..225d3d1ce 100644
--- a/src/mailman/model/messagestore.py
+++ b/src/mailman/model/messagestore.py
@@ -54,18 +54,19 @@ class MessageStore:
def add(self, store, message):
# Ensure that the message has the requisite headers.
message_ids = message.get_all('message-id', [])
- if len(message_ids) <> 1:
+ if len(message_ids) != 1:
raise ValueError('Exactly one Message-ID header required')
# Calculate and insert the X-Message-ID-Hash.
message_id = message_ids[0]
# Complain if the Message-ID already exists in the storage.
- existing = store.query(Message).filter(Message.message_id == message_id).first()
+ existing = store.query(Message).filter(
+ Message.message_id == message_id).first()
if existing is not None:
raise ValueError(
'Message ID already exists in message store: {0}'.format(
message_id))
shaobj = hashlib.sha1(message_id)
- hash32 = base64.b32encode(shaobj.digest())
+ hash32 = base64.b32encode(shaobj.digest()).decode('ascii')
del message['X-Message-ID-Hash']
message['X-Message-ID-Hash'] = hash32
# Calculate the path on disk where we're going to store this message
@@ -80,9 +81,9 @@ class MessageStore:
# providing a unique serial number, but to get this information, we
# have to use a straight insert instead of relying on Elixir to create
# the object.
- row = Message(message_id=message_id,
- message_id_hash=hash32,
- path=relpath)
+ Message(message_id=message_id,
+ message_id_hash=hash32,
+ path=relpath)
# Now calculate the full file system path.
path = os.path.join(config.MESSAGES_DIR, relpath)
# Write the file to the path, but catch the appropriate exception in
@@ -95,7 +96,7 @@ class MessageStore:
pickle.dump(message, fp, -1)
break
except IOError as error:
- if error.errno <> errno.ENOENT:
+ if error.errno != errno.ENOENT:
raise
makedirs(os.path.dirname(path))
return hash32
@@ -114,13 +115,10 @@ class MessageStore:
@dbconnection
def get_message_by_hash(self, store, message_id_hash):
- # It's possible the hash came from a message header, in which case it
- # will be a Unicode. However when coming from source code, it may be
- # an 8-string. Coerce to the latter if necessary; it must be
- # US-ASCII.
- if isinstance(message_id_hash, unicode):
- message_id_hash = message_id_hash.encode('ascii')
- row = store.query(Message).filter_by(message_id_hash=message_id_hash).first()
+ if isinstance(message_id_hash, bytes):
+ message_id_hash = message_id_hash.decode('utf-8')
+ row = store.query(Message).filter_by(
+ message_id_hash=message_id_hash).first()
if row is None:
return None
return self._get_message(row)
diff --git a/src/mailman/model/mime.py b/src/mailman/model/mime.py
index 3eac4f07b..906af91ea 100644
--- a/src/mailman/model/mime.py
+++ b/src/mailman/model/mime.py
@@ -25,7 +25,7 @@ __all__ = [
]
-from sqlalchemy import Column, Integer, Unicode, ForeignKey
+from sqlalchemy import Column, ForeignKey, Integer, Unicode
from sqlalchemy.orm import relationship
from zope.interface import implementer
@@ -46,7 +46,7 @@ class ContentFilter(Model):
mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
mailing_list = relationship('MailingList')
- filter_type = Column(Enum(enum=FilterType))
+ filter_type = Column(Enum(FilterType))
filter_pattern = Column(Unicode)
def __init__(self, mailing_list, filter_pattern, filter_type):
diff --git a/src/mailman/model/pending.py b/src/mailman/model/pending.py
index 97d394721..68a8cd63e 100644
--- a/src/mailman/model/pending.py
+++ b/src/mailman/model/pending.py
@@ -32,7 +32,7 @@ import hashlib
from lazr.config import as_timedelta
from sqlalchemy import (
- Column, Integer, Unicode, ForeignKey, DateTime, LargeBinary)
+ Column, DateTime, ForeignKey, Integer, LargeBinary, Unicode)
from sqlalchemy.orm import relationship
from zope.interface import implementer
from zope.interface.verify import verifyObject
@@ -53,15 +53,15 @@ class PendedKeyValue(Model):
__tablename__ = 'pendedkeyvalue'
- def __init__(self, key, value):
- self.key = key
- self.value = value
-
id = Column(Integer, primary_key=True)
key = Column(Unicode)
value = Column(Unicode)
pended_id = Column(Integer, ForeignKey('pended.id'))
+ def __init__(self, key, value):
+ self.key = key
+ self.value = value
+
@implementer(IPended)
@@ -70,15 +70,17 @@ class Pended(Model):
__tablename__ = 'pended'
- def __init__(self, token, expiration_date):
- self.token = token
- self.expiration_date = expiration_date
-
id = Column(Integer, primary_key=True)
token = Column(LargeBinary) # TODO : was RawStr()
expiration_date = Column(DateTime)
key_values = relationship('PendedKeyValue')
+ def __init__(self, token, expiration_date):
+ super(Pended, self).__init__()
+ self.token = token
+ self.expiration_date = expiration_date
+
+
@implementer(IPendable)
class UnpendedPendable(dict):
@@ -118,10 +120,10 @@ class Pendings:
token=token,
expiration_date=now() + lifetime)
for key, value in pendable.items():
- if isinstance(key, str):
- key = unicode(key, 'utf-8')
- if isinstance(value, str):
- value = unicode(value, 'utf-8')
+ if isinstance(key, bytes):
+ key = key.decode('utf-8')
+ if isinstance(value, bytes):
+ value = value.decode('utf-8')
elif type(value) is int:
value = '__builtin__.int\1%s' % value
elif type(value) is float:
@@ -150,8 +152,9 @@ class Pendings:
pendable = UnpendedPendable()
# Find all PendedKeyValue entries that are associated with the pending
# object's ID. Watch out for type conversions.
- for keyvalue in store.query(PendedKeyValue).filter(
- PendedKeyValue.pended_id == pending.id):
+ entries = store.query(PendedKeyValue).filter(
+ PendedKeyValue.pended_id == pending.id)
+ for keyvalue in entries:
if keyvalue.value is not None and '\1' in keyvalue.value:
type_name, value = keyvalue.value.split('\1', 1)
pendable[keyvalue.key] = call_name(type_name, value)
@@ -171,7 +174,7 @@ class Pendings:
# Find all PendedKeyValue entries that are associated with the
# pending object's ID.
q = store.query(PendedKeyValue).filter(
- PendedKeyValue.pended_id == pending.id)
+ PendedKeyValue.pended_id == pending.id)
for keyvalue in q:
store.delete(keyvalue)
store.delete(pending)
diff --git a/src/mailman/model/preferences.py b/src/mailman/model/preferences.py
index d74b17e30..1278f80b7 100644
--- a/src/mailman/model/preferences.py
+++ b/src/mailman/model/preferences.py
@@ -25,7 +25,7 @@ __all__ = [
]
-from sqlalchemy import Column, Integer, Unicode, Boolean
+from sqlalchemy import Boolean, Column, Integer, Unicode
from zope.component import getUtility
from zope.interface import implementer
@@ -49,8 +49,8 @@ class Preferences(Model):
_preferred_language = Column('preferred_language', Unicode)
receive_list_copy = Column(Boolean)
receive_own_postings = Column(Boolean)
- delivery_mode = Column(Enum(enum=DeliveryMode))
- delivery_status = Column(Enum(enum=DeliveryStatus))
+ delivery_mode = Column(Enum(DeliveryMode))
+ delivery_status = Column(Enum(DeliveryStatus))
def __repr__(self):
return '<Preferences object at {0:#x}>'.format(id(self))
diff --git a/src/mailman/model/requests.py b/src/mailman/model/requests.py
index 3d15ddea9..335e1e002 100644
--- a/src/mailman/model/requests.py
+++ b/src/mailman/model/requests.py
@@ -26,7 +26,7 @@ __all__ = [
from cPickle import dumps, loads
from datetime import timedelta
-from sqlalchemy import Column, Unicode, Integer, ForeignKey, LargeBinary
+from sqlalchemy import Column, ForeignKey, Integer, LargeBinary, Unicode
from sqlalchemy.orm import relationship
from zope.component import getUtility
from zope.interface import implementer
@@ -69,7 +69,8 @@ class ListRequests:
@property
@dbconnection
def count(self, store):
- return store.query(_Request).filter_by(mailing_list=self.mailing_list).count()
+ return store.query(_Request).filter_by(
+ mailing_list=self.mailing_list).count()
@dbconnection
def count_of(self, store, request_type):
@@ -79,7 +80,8 @@ class ListRequests:
@property
@dbconnection
def held_requests(self, store):
- results = store.query(_Request).filter_by(mailing_list=self.mailing_list)
+ results = store.query(_Request).filter_by(
+ mailing_list=self.mailing_list)
for request in results:
yield request
@@ -148,13 +150,14 @@ class _Request(Model):
id = Column(Integer, primary_key=True)# TODO: ???, default=AutoReload)
key = Column(Unicode)
- request_type = Column(Enum(enum=RequestType))
+ request_type = Column(Enum(RequestType))
data_hash = Column(LargeBinary)
mailing_list_id = Column(Integer, ForeignKey('mailinglist.id'))
mailing_list = relationship('MailingList')
def __init__(self, key, request_type, mailing_list, data_hash):
+ super(_Request, self).__init__()
self.key = key
self.request_type = request_type
self.mailing_list = mailing_list
diff --git a/src/mailman/model/roster.py b/src/mailman/model/roster.py
index a9a396523..a6cbeb104 100644
--- a/src/mailman/model/roster.py
+++ b/src/mailman/model/roster.py
@@ -161,7 +161,7 @@ class AdministratorRoster(AbstractRoster):
return store.query(Member).filter(
Member.list_id == self._mlist.list_id,
or_(Member.role == MemberRole.owner,
- Member.role == MemberRole.moderator))
+ Member.role == MemberRole.moderator))
@dbconnection
def get_member(self, store, address):
@@ -169,7 +169,7 @@ class AdministratorRoster(AbstractRoster):
results = store.query(Member).filter(
Member.list_id == self._mlist.list_id,
or_(Member.role == MemberRole.moderator,
- Member.role == MemberRole.owner),
+ Member.role == MemberRole.owner),
Address.email == address,
Member.address_id == Address.id)
if results.count() == 0:
diff --git a/src/mailman/model/tests/test_listmanager.py b/src/mailman/model/tests/test_listmanager.py
index 3951e8250..b290138f3 100644
--- a/src/mailman/model/tests/test_listmanager.py
+++ b/src/mailman/model/tests/test_listmanager.py
@@ -29,11 +29,11 @@ __all__ = [
import unittest
-from sqlalchemy.orm import sessionmaker
from zope.component import getUtility
from mailman.app.lifecycle import create_list
from mailman.app.moderator import hold_message
+from mailman.config import config
from mailman.interfaces.listmanager import (
IListManager, ListCreatedEvent, ListCreatingEvent, ListDeletedEvent,
ListDeletingEvent)
@@ -148,9 +148,8 @@ Message-ID: <argon>
for name in filter_names:
setattr(self._ant, name, ['test-filter-1', 'test-filter-2'])
getUtility(IListManager).delete(self._ant)
- Session = sessionmaker()
- store = Session.object_session(self._ant)
- filters = store.query(ContentFilter).filter_by(mailing_list = self._ant)
+ filters = config.db.store.query(ContentFilter).filter_by(
+ mailing_list = self._ant)
self.assertEqual(filters.count(), 0)
diff --git a/src/mailman/model/tests/test_requests.py b/src/mailman/model/tests/test_requests.py
index dc1b9b849..419c6077f 100644
--- a/src/mailman/model/tests/test_requests.py
+++ b/src/mailman/model/tests/test_requests.py
@@ -70,10 +70,10 @@ Something else.
# Calling hold_request() with a bogus request type is an error.
with self.assertRaises(TypeError) as cm:
self._requests_db.hold_request(5, 'foo')
- self.assertEqual(cm.exception.message, 5)
+ self.assertEqual(cm.exception.args[0], 5)
def test_delete_missing_request(self):
# Trying to delete a missing request is an error.
with self.assertRaises(KeyError) as cm:
self._requests_db.delete_request(801)
- self.assertEqual(cm.exception.message, 801)
+ self.assertEqual(cm.exception.args[0], 801)
diff --git a/src/mailman/model/uid.py b/src/mailman/model/uid.py
index 77f1b59bb..29d8e7021 100644
--- a/src/mailman/model/uid.py
+++ b/src/mailman/model/uid.py
@@ -29,8 +29,8 @@ __all__ = [
from sqlalchemy import Column, Integer
from mailman.database.model import Model
-from mailman.database.types import UUID
from mailman.database.transaction import dbconnection
+from mailman.database.types import UUID
@@ -54,6 +54,7 @@ class UID(Model):
@dbconnection
def __init__(self, store, uid):
+ super(UID, self).__init__()
self.uid = uid
store.add(self)
diff --git a/src/mailman/model/user.py b/src/mailman/model/user.py
index cd47a5dac..576015dbe 100644
--- a/src/mailman/model/user.py
+++ b/src/mailman/model/user.py
@@ -25,7 +25,7 @@ __all__ = [
]
from sqlalchemy import (
- Column, Unicode, Integer, DateTime, ForeignKey, LargeBinary)
+ Column, DateTime, ForeignKey, Integer, LargeBinary, Unicode)
from sqlalchemy.orm import relationship, backref
from zope.event import notify
from zope.interface import implementer
@@ -60,25 +60,24 @@ class User(Model):
_user_id = Column(UUID)
_created_on = Column(DateTime)
- addresses = relationship('Address',
- backref='user',
- primaryjoin=
- id==Address.user_id)
+ addresses = relationship(
+ 'Address', backref='user',
+ primaryjoin=(id==Address.user_id))
- _preferred_address_id = Column(Integer, ForeignKey('address.id',
- use_alter=True,
- name='_preferred_address'))
- _preferred_address = relationship('Address',
- primaryjoin=
- _preferred_address_id==Address.id,
- post_update=True)
+ _preferred_address_id = Column(
+ Integer,
+ ForeignKey('address.id', use_alter=True, name='_preferred_address'))
+ _preferred_address = relationship(
+ 'Address', primaryjoin=(_preferred_address_id==Address.id),
+ post_update=True)
preferences_id = Column(Integer, ForeignKey('preferences.id'))
- preferences = relationship('Preferences',
- backref=backref('user', uselist=False))
+ preferences = relationship(
+ 'Preferences', backref=backref('user', uselist=False))
@dbconnection
def __init__(self, store, display_name=None, preferences=None):
+ super(User, self).__init__()
self._created_on = date_factory.now()
user_id = uid_factory.new_uid()
assert store.query(User).filter_by(_user_id=user_id).count() == 0, (
diff --git a/src/mailman/model/version.py b/src/mailman/model/version.py
deleted file mode 100644
index 95cf03dac..000000000
--- a/src/mailman/model/version.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Copyright (C) 2007-2014 by the Free Software Foundation, Inc.
-#
-# This file is part of GNU Mailman.
-#
-# GNU Mailman 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 3 of the License, or (at your option)
-# any later version.
-#
-# GNU Mailman 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
-# GNU Mailman. If not, see <http://www.gnu.org/licenses/>.
-
-"""Model class for version numbers."""
-
-from __future__ import absolute_import, print_function, unicode_literals
-
-__metaclass__ = type
-__all__ = [
- 'Version',
- ]
-
-from sqlalchemy import Column, Unicode, Integer
-
-from mailman.database.model import Model
-
-
-
-class Version(Model):
-
- __tablename__ = 'version'
-
- id = Column(Integer, primary_key=True)
- component = Column(Unicode)
- version = Column(Unicode)
-
- # The testing machinery will generally reset all tables, however because
- # this table tracks schema migrations, we do not want to reset it.
- PRESERVE = True
-
- def __init__(self, component, version):
- self.component = component
- self.version = version
diff --git a/src/mailman/rest/validator.py b/src/mailman/rest/validator.py
index 90d0334e9..8fe1a6078 100644
--- a/src/mailman/rest/validator.py
+++ b/src/mailman/rest/validator.py
@@ -54,7 +54,7 @@ class enum_validator:
return self._enum_class[enum_value]
except KeyError as exception:
# Retain the error message.
- raise ValueError(exception.message)
+ raise ValueError(exception.args[0])
def subscriber_validator(subscriber):
diff --git a/src/mailman/utilities/importer.py b/src/mailman/utilities/importer.py
index 9856a8223..26b7261e3 100644
--- a/src/mailman/utilities/importer.py
+++ b/src/mailman/utilities/importer.py
@@ -186,7 +186,6 @@ NAME_MAPPINGS = dict(
filter_mime_types='filter_types',
generic_nonmember_action='default_nonmember_action',
include_list_post_header='allow_list_posts',
- last_post_time='last_post_at',
member_moderation_action='default_member_action',
mod_password='moderator_password',
news_moderation='newsgroup_moderation',
@@ -198,13 +197,13 @@ NAME_MAPPINGS = dict(
send_welcome_msg='send_welcome_message',
)
-# Datetime Fields that need a type conversion to python datetime
-# object for SQLite database.
-DATETIME_OBJECTS = [
+# These DateTime fields of the mailinglist table need a type conversion to
+# Python datetime object for SQLite databases.
+DATETIME_COLUMNS = [
'created_at',
'digest_last_sent_at',
'last_post_time',
-]
+ ]
EXCLUDES = set((
'digest_members',
@@ -225,8 +224,8 @@ def import_config_pck(mlist, config_dict):
# Some attributes must not be directly imported.
if key in EXCLUDES:
continue
- # Created at must not be set, it needs a type conversion
- if key in DATETIME_OBJECTS:
+ # These objects need explicit type conversions.
+ if key in DATETIME_COLUMNS:
continue
# Some attributes from Mailman 2 were renamed in Mailman 3.
key = NAME_MAPPINGS.get(key, key)
@@ -249,7 +248,7 @@ def import_config_pck(mlist, config_dict):
except (TypeError, KeyError):
print('Type conversion error for key "{}": {}'.format(
key, value), file=sys.stderr)
- for key in DATETIME_OBJECTS:
+ for key in DATETIME_COLUMNS:
try:
value = datetime.datetime.utcfromtimestamp(config_dict[key])
except KeyError: