summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mailman/archiving/mailarchive.py1
-rw-r--r--src/mailman/archiving/mhonarc.py1
-rw-r--r--src/mailman/archiving/prototype.py1
-rw-r--r--src/mailman/config/config.py10
-rw-r--r--src/mailman/config/configure.zcml6
-rw-r--r--src/mailman/config/schema.cfg2
-rw-r--r--src/mailman/config/tests/test_archivers.py56
-rw-r--r--src/mailman/database/schema/sqlite_20130406000000_01.sql20
-rw-r--r--src/mailman/database/tests/test_migrations.py27
-rw-r--r--src/mailman/interfaces/archiver.py2
-rw-r--r--src/mailman/interfaces/listmanager.py8
-rw-r--r--src/mailman/interfaces/mailinglist.py47
-rw-r--r--src/mailman/model/mailinglist.py107
-rw-r--r--src/mailman/model/tests/test_domain.py2
-rw-r--r--src/mailman/model/tests/test_listmanager.py5
-rw-r--r--src/mailman/model/tests/test_mailinglist.py102
-rw-r--r--src/mailman/rest/configuration.py16
-rw-r--r--src/mailman/runners/archive.py39
-rw-r--r--src/mailman/runners/tests/test_archiver.py14
19 files changed, 373 insertions, 93 deletions
diff --git a/src/mailman/archiving/mailarchive.py b/src/mailman/archiving/mailarchive.py
index 34a10fd25..a8489d02e 100644
--- a/src/mailman/archiving/mailarchive.py
+++ b/src/mailman/archiving/mailarchive.py
@@ -43,6 +43,7 @@ class MailArchive:
"""
name = 'mail-archive'
+ is_enabled = False
def __init__(self):
# Read our specific configuration file
diff --git a/src/mailman/archiving/mhonarc.py b/src/mailman/archiving/mhonarc.py
index 6f8f3e168..646030f5e 100644
--- a/src/mailman/archiving/mhonarc.py
+++ b/src/mailman/archiving/mhonarc.py
@@ -46,6 +46,7 @@ class MHonArc:
"""Local MHonArc archiver."""
name = 'mhonarc'
+ is_enabled = False
def __init__(self):
# Read our specific configuration file
diff --git a/src/mailman/archiving/prototype.py b/src/mailman/archiving/prototype.py
index df215a0da..60fa4c59f 100644
--- a/src/mailman/archiving/prototype.py
+++ b/src/mailman/archiving/prototype.py
@@ -52,6 +52,7 @@ class Prototype:
"""
name = 'prototype'
+ is_enabled = False
@staticmethod
def list_url(mlist):
diff --git a/src/mailman/config/config.py b/src/mailman/config/config.py
index 74931c029..86919e3f1 100644
--- a/src/mailman/config/config.py
+++ b/src/mailman/config/config.py
@@ -249,12 +249,14 @@ class Configuration:
@property
def archivers(self):
- """Iterate over all the enabled archivers."""
+ """Iterate over all the archivers."""
for section in self._config.getByCategory('archiver', []):
- if not as_boolean(section.enable):
+ class_path = section['class'].strip()
+ if len(class_path) == 0:
continue
- class_path = section['class']
- yield call_name(class_path)
+ archiver = call_name(class_path)
+ archiver.is_enabled = as_boolean(section.enable)
+ yield archiver
@property
def style_configs(self):
diff --git a/src/mailman/config/configure.zcml b/src/mailman/config/configure.zcml
index efb449538..f9b9cb093 100644
--- a/src/mailman/config/configure.zcml
+++ b/src/mailman/config/configure.zcml
@@ -30,6 +30,12 @@
<adapter
for="mailman.interfaces.mailinglist.IMailingList"
+ provides="mailman.interfaces.mailinglist.IListArchiverSet"
+ factory="mailman.model.mailinglist.ListArchiverSet"
+ />
+
+ <adapter
+ for="mailman.interfaces.mailinglist.IMailingList"
provides="mailman.interfaces.requests.IListRequests"
factory="mailman.model.requests.ListRequests"
/>
diff --git a/src/mailman/config/schema.cfg b/src/mailman/config/schema.cfg
index f132b0358..33fd99cb3 100644
--- a/src/mailman/config/schema.cfg
+++ b/src/mailman/config/schema.cfg
@@ -532,7 +532,7 @@ register_bounces_every: 15m
# following values.
# The class implementing the IArchiver interface.
-class: mailman.archiving.prototype.Prototype
+class:
# Set this to 'yes' to enable the archiver.
enable: no
diff --git a/src/mailman/config/tests/test_archivers.py b/src/mailman/config/tests/test_archivers.py
new file mode 100644
index 000000000..cd84714d5
--- /dev/null
+++ b/src/mailman/config/tests/test_archivers.py
@@ -0,0 +1,56 @@
+# Copyright (C) 2013 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/>.
+
+"""Site-wide archiver configuration tests."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'TestArchivers',
+ ]
+
+
+import unittest
+
+from mailman.config import config
+from mailman.testing.helpers import configuration
+from mailman.testing.layers import ConfigLayer
+
+
+
+class TestArchivers(unittest.TestCase):
+ layer = ConfigLayer
+
+ def test_enabled(self):
+ # By default, the testing configuration enables the archivers.
+ archivers = {}
+ for archiver in config.archivers:
+ archivers[archiver.name] = archiver
+ self.assertTrue(archivers['prototype'].is_enabled)
+ self.assertTrue(archivers['mail-archive'].is_enabled)
+ self.assertTrue(archivers['mhonarc'].is_enabled)
+
+ @configuration('archiver.mhonarc', enable='no')
+ def test_disabled(self):
+ # We just disabled one of the archivers.
+ archivers = {}
+ for archiver in config.archivers:
+ archivers[archiver.name] = archiver
+ self.assertTrue(archivers['prototype'].is_enabled)
+ self.assertTrue(archivers['mail-archive'].is_enabled)
+ self.assertFalse(archivers['mhonarc'].is_enabled)
diff --git a/src/mailman/database/schema/sqlite_20130406000000_01.sql b/src/mailman/database/schema/sqlite_20130406000000_01.sql
index 9bdc2aae0..fe30ed247 100644
--- a/src/mailman/database/schema/sqlite_20130406000000_01.sql
+++ b/src/mailman/database/schema/sqlite_20130406000000_01.sql
@@ -6,12 +6,17 @@
-- For SQLite3 migration strategy, see
-- http://sqlite.org/faq.html#q11
--- REMOVALS from the bounceevent table:
+-- ADD listarchiver table.
+
+-- REMOVALs from the bounceevent table:
-- REM list_name
--- ADDS to the ban bounceevent table:
+-- 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,
@@ -28,3 +33,14 @@ INSERT INTO bounceevent_backup SELECT
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/tests/test_migrations.py b/src/mailman/database/tests/test_migrations.py
index d983f9891..44f594ba7 100644
--- a/src/mailman/database/tests/test_migrations.py
+++ b/src/mailman/database/tests/test_migrations.py
@@ -36,6 +36,7 @@ import unittest
from datetime import datetime
from operator import attrgetter
from pkg_resources import resource_string
+from sqlite3 import OperationalError
from storm.exceptions import DatabaseError
from zope.component import getUtility
@@ -65,6 +66,23 @@ class MigrationTestBase(unittest.TestCase):
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.
@@ -450,6 +468,15 @@ class TestMigration20130406Schema(MigrationTestBase):
('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',))
+
class TestMigration20130406MigratedData(MigrationTestBase):
diff --git a/src/mailman/interfaces/archiver.py b/src/mailman/interfaces/archiver.py
index 5f074503e..aac372865 100644
--- a/src/mailman/interfaces/archiver.py
+++ b/src/mailman/interfaces/archiver.py
@@ -50,6 +50,8 @@ class IArchiver(Interface):
"""An interface to the archiver."""
name = Attribute('The name of this archiver')
+ is_enabled = Attribute(
+ 'A flag indicating whether this archiver is enabled site-wide.')
def list_url(mlist):
"""Return the url to the top of the list's archive.
diff --git a/src/mailman/interfaces/listmanager.py b/src/mailman/interfaces/listmanager.py
index 45b12af53..837abf310 100644
--- a/src/mailman/interfaces/listmanager.py
+++ b/src/mailman/interfaces/listmanager.py
@@ -97,9 +97,9 @@ class IListManager(Interface):
def create(fqdn_listname):
"""Create a mailing list with the given name.
- :type fqdn_listname: Unicode
:param fqdn_listname: The fully qualified name of the mailing list,
e.g. `mylist@example.com`.
+ :type fqdn_listname: Unicode
:return: The newly created `IMailingList`.
:raise `ListAlreadyExistsError` if the named list already exists.
"""
@@ -107,8 +107,8 @@ class IListManager(Interface):
def get(fqdn_listname):
"""Return the mailing list with the given name, if it exists.
- :type fqdn_listname: Unicode.
:param fqdn_listname: The fully qualified name of the mailing list.
+ :type fqdn_listname: Unicode.
:return: the matching `IMailingList` or None if the named list does
not exist.
"""
@@ -116,8 +116,8 @@ class IListManager(Interface):
def get_by_list_id(list_id):
"""Return the mailing list with the given list id, if it exists.
- :type fqdn_listname: Unicode.
:param fqdn_listname: The fully qualified name of the mailing list.
+ :type fqdn_listname: Unicode.
:return: the matching `IMailingList` or None if the named list does
not exist.
"""
@@ -125,8 +125,8 @@ class IListManager(Interface):
def delete(mlist):
"""Remove the mailing list from the database.
- :type mlist: `IMailingList`
:param mlist: The mailing list to delete.
+ :type mlist: `IMailingList`
"""
mailing_lists = Attribute(
diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py
index 8519238db..b12d84ec9 100644
--- a/src/mailman/interfaces/mailinglist.py
+++ b/src/mailman/interfaces/mailinglist.py
@@ -23,8 +23,9 @@ __metaclass__ = type
__all__ = [
'IAcceptableAlias',
'IAcceptableAliasSet',
+ 'IListArchiver',
+ 'IListArchiverSet',
'IMailingList',
- 'IArchiverList',
'Personalization',
'ReplyToMunging',
]
@@ -55,16 +56,6 @@ class ReplyToMunging(Enum):
# An explicit Reply-To header is added
explicit_header = 2
-class IArchiverList(Interface):
- mailing_list_id = Attribute("""List id""")
- archiver_name = Attribute("""Archiver name""")
- archiver_enabled = Attribute("""If is enabled.""")
-
-class IListArchiverSet(Interface):
- def getAll():
- """Return dict containing all archivers and their settings."""
- def set(archiver, is_enabled):
- """Set archiver for this list."""
class IMailingList(Interface):
@@ -802,3 +793,37 @@ class IAcceptableAliasSet(Interface):
aliases = Attribute(
"""An iterator over all the acceptable aliases.""")
+
+
+
+class IListArchiver(Interface):
+ """An archiver for a mailing list.
+
+ The named archiver must be enabled site-wide in order for a mailing list
+ to be able to enable it.
+ """
+
+ mailing_list = Attribute('The associated mailing list.')
+
+ name = Attribute('The name of the archiver.')
+
+ is_enabled = Attribute('Is this archiver enabled for this mailing list?')
+
+ system_archiver = Attribute(
+ 'The associated system-wide IArchiver instance.')
+
+
+class IListArchiverSet(Interface):
+ """The set of archivers (enabled or disabled) for a mailing list."""
+
+ archivers = Attribute(
+ """An iterator over all the archivers for this mailing list.""")
+
+ def get(archiver_name):
+ """Return the `IListArchiver` with the given name, if it exists.
+
+ :param archiver_name: The name of the archiver.
+ :type archiver_name: unicode.
+ :return: the matching `IListArchiver` or None if the named archiver
+ does not exist.
+ """
diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py
index a0239caa9..e9601e412 100644
--- a/src/mailman/model/mailinglist.py
+++ b/src/mailman/model/mailinglist.py
@@ -28,8 +28,8 @@ __all__ = [
import os
from storm.locals import (
- And, Bool, DateTime, Float, Int, Pickle, RawStr, Reference, ReferenceSet,
- Store, TimeDelta, Unicode)
+ And, Bool, DateTime, Float, Int, Pickle, RawStr, Reference, Store,
+ TimeDelta, Unicode)
from urlparse import urljoin
from zope.component import getUtility
from zope.event import notify
@@ -47,7 +47,7 @@ from mailman.interfaces.digests import DigestFrequency
from mailman.interfaces.domain import IDomainManager
from mailman.interfaces.languages import ILanguageManager
from mailman.interfaces.mailinglist import (
- IAcceptableAlias, IAcceptableAliasSet, IArchiverList, IListArchiverSet,
+ IAcceptableAlias, IAcceptableAliasSet, IListArchiver, IListArchiverSet,
IMailingList, Personalization, ReplyToMunging)
from mailman.interfaces.member import (
AlreadySubscribedError, MemberRole, MissingPreferredAddressError,
@@ -67,19 +67,6 @@ from mailman.utilities.string import expand
SPACE = ' '
UNDERSCORE = '_'
-@implementer(IArchiverList)
-class ArchiverList(Model):
- __storm_primary__ = "mailing_list_id", "archiver_name"
- mailing_list_id = Int()
- archiver_name = Unicode()
- archiver_enabled = Bool()
-
- def __init__(self, mailing_list_id, archiver_name):
- self.mailing_list_id = mailing_list_id
- self.archiver_name = archiver_name
- self.archiver_enabled = False
-
-
@implementer(IMailingList)
@@ -91,7 +78,6 @@ class MailingList(Model):
# XXX denotes attributes that should be part of the public interface but
# are currently missing.
- archivers = ReferenceSet(id, ArchiverList.mailing_list_id)
# List identity
list_name = Unicode()
mail_host = Unicode()
@@ -553,39 +539,68 @@ class AcceptableAliasSet:
for alias in aliases:
yield alias.alias
-@implementer(IListArchiverSet)
-class ListArchiverSet:
- def __init__(self, mailing_list):
- self._mailing_list = mailing_list
- self.lazyAdd()
- def getAll(self):
- entries = Store.of(self._mailing_list).find(ArchiverList, ArchiverList.mailing_list_id == self._mailing_list.id)
- all_in_config = {archiver.name for archiver in config.archivers}
- ret = {}
- for entry in entries:
- if entry.archiver_name in all_in_config:
- ret[entry.archiver_name] = int(entry.archiver_enabled)
- return ret
+
+@implementer(IListArchiver)
+class ListArchiver(Model):
+ """See `IListArchiver`."""
+
+ id = Int(primary=True)
+
+ mailing_list_id = Int()
+ mailing_list = Reference(mailing_list_id, MailingList.id)
+ name = Unicode()
+ _is_enabled = Bool()
+
+ def __init__(self, mailing_list, archiver_name, system_archiver):
+ self.mailing_list = mailing_list
+ self.name = archiver_name
+ self._is_enabled = system_archiver.is_enabled
- def set(self, archiver, is_enabled):
- bool_enabled = (int(is_enabled) != 0)
- self.get(archiver).set(archiver_enabled=bool_enabled)
+ @property
+ def system_archiver(self):
+ for archiver in config.archivers:
+ if archiver.name == self.name:
+ return archiver
+ return None
+
+ @property
+ def is_enabled(self):
+ return self.system_archiver.is_enabled and self._is_enabled
- def isEnabled(self, archiverName):
- return self.get(archiverName).one().archiver_enabled
+ @is_enabled.setter
+ def is_enabled(self, value):
+ self._is_enabled = value
- def get(self, archiverName):
- return Store.of(self._mailing_list).find(ArchiverList,
- (ArchiverList.mailing_list_id == self._mailing_list.id) & (ArchiverList.archiver_name == archiverName))
- def lazyAdd(self):
- names = []
+@implementer(IListArchiverSet)
+class ListArchiverSet:
+ def __init__(self, mailing_list):
+ self._mailing_list = mailing_list
+ system_archivers = {}
for archiver in config.archivers:
- count = self.get(archiver.name).count()
- names.append((archiver.name, count))
- if not count:
- entry = ArchiverList(self._mailing_list.id, archiver.name)
- Store.of(self._mailing_list).add(entry)
- Store.of(self._mailing_list).commit()
+ system_archivers[archiver.name] = archiver
+ # Add any system enabled archivers which aren't already associated
+ # with the mailing list.
+ store = Store.of(self._mailing_list)
+ for archiver_name in system_archivers:
+ exists = store.find(
+ ListArchiver,
+ And(ListArchiver.mailing_list == mailing_list,
+ ListArchiver.name == archiver_name)).one()
+ if exists is None:
+ store.add(ListArchiver(mailing_list, archiver_name,
+ system_archivers[archiver_name]))
+
+ @property
+ def archivers(self):
+ entries = Store.of(self._mailing_list).find(
+ ListArchiver, ListArchiver.mailing_list == self._mailing_list)
+ for entry in entries:
+ yield entry
+ def get(self, archiver_name):
+ return Store.of(self._mailing_list).find(
+ ListArchiver,
+ And(ListArchiver.mailing_list == self._mailing_list,
+ ListArchiver.name == archiver_name)).one()
diff --git a/src/mailman/model/tests/test_domain.py b/src/mailman/model/tests/test_domain.py
index 3d7f95615..67924d393 100644
--- a/src/mailman/model/tests/test_domain.py
+++ b/src/mailman/model/tests/test_domain.py
@@ -21,6 +21,8 @@ from __future__ import absolute_import, unicode_literals
__metaclass__ = type
__all__ = [
+ 'TestDomainLifecycleEvents',
+ 'TestDomainManager',
]
diff --git a/src/mailman/model/tests/test_listmanager.py b/src/mailman/model/tests/test_listmanager.py
index 152d96b9f..b18c8e5d1 100644
--- a/src/mailman/model/tests/test_listmanager.py
+++ b/src/mailman/model/tests/test_listmanager.py
@@ -17,10 +17,13 @@
"""Test the ListManager."""
-from __future__ import absolute_import, unicode_literals
+from __future__ import absolute_import, print_function, unicode_literals
__metaclass__ = type
__all__ = [
+ 'TestListCreation',
+ 'TestListLifecycleEvents',
+ 'TestListManager',
]
diff --git a/src/mailman/model/tests/test_mailinglist.py b/src/mailman/model/tests/test_mailinglist.py
new file mode 100644
index 000000000..09c4cb38f
--- /dev/null
+++ b/src/mailman/model/tests/test_mailinglist.py
@@ -0,0 +1,102 @@
+# Copyright (C) 2013 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 MailingLists and related model objects.."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'TestListArchiver',
+ 'TestDisabledListArchiver',
+ ]
+
+
+import unittest
+
+from mailman.app.lifecycle import create_list
+from mailman.config import config
+from mailman.interfaces.mailinglist import IListArchiverSet
+from mailman.testing.helpers import configuration
+from mailman.testing.layers import ConfigLayer
+
+
+
+class TestListArchiver(unittest.TestCase):
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('ant@example.com')
+ self._set = IListArchiverSet(self._mlist)
+
+ def test_list_archivers(self):
+ # Find the set of archivers registered for this mailing list.
+ self.assertEqual(
+ ['mail-archive', 'mhonarc', 'prototype'],
+ sorted(archiver.name for archiver in self._set.archivers))
+
+ def test_get_archiver(self):
+ # Use .get() to see if a mailing list has an archiver.
+ archiver = self._set.get('prototype')
+ self.assertEqual(archiver.name, 'prototype')
+ self.assertTrue(archiver.is_enabled)
+ self.assertEqual(archiver.mailing_list, self._mlist)
+ self.assertEqual(archiver.system_archiver.name, 'prototype')
+
+ def test_get_archiver_no_such(self):
+ # Using .get() on a non-existing name returns None.
+ self.assertIsNone(self._set.get('no-such-archiver'))
+
+ def test_site_disabled(self):
+ # Here the system configuration enables all the archivers in time for
+ # the archive set to be created with all list archivers enabled. But
+ # then the site-wide archiver gets disabled, so the list specific
+ # archiver will also be disabled.
+ archiver_set = IListArchiverSet(self._mlist)
+ archiver = archiver_set.get('prototype')
+ self.assertTrue(archiver.is_enabled)
+ # Disable the site-wide archiver.
+ archiver.system_archiver.is_enabled = False
+ self.assertFalse(archiver.is_enabled)
+
+
+
+class TestDisabledListArchiver(unittest.TestCase):
+ layer = ConfigLayer
+
+ def setUp(self):
+ self._mlist = create_list('ant@example.com')
+
+ @configuration('archiver.prototype', enable='no')
+ def test_enable_list_archiver(self):
+ # When the system configuration file disables an archiver site-wide,
+ # the list-specific mailing list will get initialized as not enabled.
+ # Create the archiver set on the fly so that it doesn't get
+ # initialized with a configuration that enables the prototype archiver.
+ archiver_set = IListArchiverSet(self._mlist)
+ archiver = archiver_set.get('prototype')
+ self.assertFalse(archiver.is_enabled)
+ # Enable both the list archiver and the system archiver.
+ archiver.is_enabled = True
+ config.push('enable prototype', """\
+ [archiver.prototype]
+ enable: yes
+ """)
+ # Get the IListArchiver again.
+ archiver = archiver_set.get('prototype')
+ self.assertTrue(archiver.is_enabled)
+ config.pop('enable prototype')
diff --git a/src/mailman/rest/configuration.py b/src/mailman/rest/configuration.py
index eec248ad0..560aafbde 100644
--- a/src/mailman/rest/configuration.py
+++ b/src/mailman/rest/configuration.py
@@ -26,6 +26,7 @@ __all__ = [
from lazr.config import as_boolean, as_timedelta
+from operator import attrgetter
from restish import http, resource
from mailman.config import config
@@ -34,10 +35,10 @@ from mailman.core.errors import (
from mailman.interfaces.action import Action
from mailman.interfaces.archiver import ArchivePolicy
from mailman.interfaces.autorespond import ResponseAction
-from mailman.interfaces.mailinglist import IAcceptableAliasSet, IListArchiverSet, ReplyToMunging
+from mailman.interfaces.mailinglist import (
+ IAcceptableAliasSet, IListArchiverSet, ReplyToMunging)
from mailman.rest.helpers import GetterSetter, PATCH, etag, no_content
from mailman.rest.validator import PatchValidator, Validator, enum_validator
-from mailman.model.mailinglist import ListArchiverSet
@@ -65,21 +66,24 @@ class AcceptableAliases(GetterSetter):
for alias in value:
alias_set.add(unicode(alias))
+
+
class ListArchivers(GetterSetter):
+ """Resource for list-specific archivers."""
def get(self, mlist, attribute):
"""Return the mailing list's acceptable aliases."""
assert attribute == 'archivers', (
'Unexpected attribute: {0}'.format(attribute))
- archivers = ListArchiverSet(mlist)
- return archivers.getAll()
+ archiver_set = IListArchiverSet(mlist)
+ return sorted(archiver_set.archivers, key=attrgetter('name'))
def put(self, mlist, attribute, value):
assert attribute == 'archivers', (
'Unexpected attribute: {0}'.format(attribute))
- archivers = ListArchiverSet(mlist)
+ archiver_set = IListArchiverSet(mlist)
for key, value in value.iteritems():
- archivers.set(key, value)
+ archivers_set.set(key, value)
diff --git a/src/mailman/runners/archive.py b/src/mailman/runners/archive.py
index 9eb1dd0a2..907ba5707 100644
--- a/src/mailman/runners/archive.py
+++ b/src/mailman/runners/archive.py
@@ -36,7 +36,7 @@ from mailman.config import config
from mailman.core.runner import Runner
from mailman.interfaces.archiver import ClobberDate
from mailman.utilities.datetime import RFC822_DATE_FMT, now
-from mailman.model.mailinglist import ListArchiverSet
+from mailman.interfaces.mailinglist import IListArchiverSet
log = logging.getLogger('mailman.error')
@@ -91,20 +91,23 @@ class ArchiveRunner(Runner):
def _dispose(self, mlist, msg, msgdata):
received_time = msgdata.get('received_time', now(strip_tzinfo=False))
- for archiver in config.archivers:
- archSet = ListArchiverSet(mlist)
- if archSet.isEnabled(archiver.name):
- msg_copy = copy.deepcopy(msg)
- if _should_clobber(msg, msgdata, archiver.name):
- original_date = msg_copy['date']
- del msg_copy['date']
- del msg_copy['x-original-date']
- msg_copy['Date'] = received_time.strftime(RFC822_DATE_FMT)
- if original_date:
- msg_copy['X-Original-Date'] = original_date
- # A problem in one archiver should not prevent other archivers
- # from running.
- try:
- archiver.archive_message(mlist, msg_copy)
- except Exception:
- log.exception('Broken archiver: %s' % archiver.name)
+ archiver_set = IListArchiverSet(mlist)
+ for archiver in archiver_set.archivers:
+ # The archiver is disabled if either the list-specific or
+ # site-wide archiver is disabled.
+ if not archiver.is_enabled:
+ continue
+ msg_copy = copy.deepcopy(msg)
+ if _should_clobber(msg, msgdata, archiver.name):
+ original_date = msg_copy['date']
+ del msg_copy['date']
+ del msg_copy['x-original-date']
+ msg_copy['Date'] = received_time.strftime(RFC822_DATE_FMT)
+ if original_date:
+ msg_copy['X-Original-Date'] = original_date
+ # A problem in one archiver should not prevent other archivers
+ # from running.
+ try:
+ archiver.system_archiver.archive_message(mlist, msg_copy)
+ except Exception:
+ log.exception('Broken archiver: %s' % archiver.name)
diff --git a/src/mailman/runners/tests/test_archiver.py b/src/mailman/runners/tests/test_archiver.py
index 80a676dfd..f0ac00458 100644
--- a/src/mailman/runners/tests/test_archiver.py
+++ b/src/mailman/runners/tests/test_archiver.py
@@ -34,6 +34,7 @@ from zope.interface import implementer
from mailman.app.lifecycle import create_list
from mailman.config import config
from mailman.interfaces.archiver import IArchiver
+from mailman.interfaces.mailinglist import IListArchiverSet
from mailman.runners.archive import ArchiveRunner
from mailman.testing.helpers import (
configuration,
@@ -99,6 +100,7 @@ X-Message-ID-Hash: 4CMWUN6BHVCMHMDAOSJZ2Q72G5M32MWB
First post!
""")
self._runner = make_testable_runner(ArchiveRunner)
+ IListArchiverSet(self._mlist).get('dummy').is_enabled = True
def tearDown(self):
config.pop('dummy')
@@ -237,3 +239,15 @@ First post!
self.assertEqual(archived['message-id'], '<first>')
self.assertEqual(archived['date'], 'Mon, 01 Aug 2005 07:49:23 +0000')
self.assertEqual(archived['x-original-date'], None)
+
+ @configuration('archiver.dummy', enable='yes')
+ def test_disable_all_list_archivers(self):
+ # Let's disable all the archivers for the mailing list, but not the
+ # global archivers. No messages will get archived.
+ for archiver in IListArchiverSet(self._mlist).archivers:
+ archiver.is_enabled = False
+ self._archiveq.enqueue(
+ self._msg, {},
+ listname=self._mlist.fqdn_listname)
+ self._runner.run()
+ self.assertEqual(os.listdir(config.MESSAGES_DIR), [])