diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/core/tests/test_pipelines.py | 2 | ||||
| -rw-r--r-- | src/mailman/database/alembic/versions/70af5a4e5790_digests.py | 27 | ||||
| -rw-r--r-- | src/mailman/database/tests/test_migrations.py | 27 | ||||
| -rw-r--r-- | src/mailman/handlers/docs/digests.rst | 4 | ||||
| -rw-r--r-- | src/mailman/handlers/to_digest.py | 5 | ||||
| -rw-r--r-- | src/mailman/interfaces/mailinglist.py | 17 | ||||
| -rw-r--r-- | src/mailman/model/mailinglist.py | 3 | ||||
| -rw-r--r-- | src/mailman/rest/docs/listconf.rst | 9 | ||||
| -rw-r--r-- | src/mailman/rest/listconf.py | 4 | ||||
| -rw-r--r-- | src/mailman/rest/tests/test_listconf.py | 126 | ||||
| -rw-r--r-- | src/mailman/styles/base.py | 3 |
11 files changed, 211 insertions, 16 deletions
diff --git a/src/mailman/core/tests/test_pipelines.py b/src/mailman/core/tests/test_pipelines.py index 134392668..627873fcf 100644 --- a/src/mailman/core/tests/test_pipelines.py +++ b/src/mailman/core/tests/test_pipelines.py @@ -151,7 +151,7 @@ testing def test_only_decorate_output(self): # Ensure that decoration is not done on the archive, digest, or # usenet copy of the message. - self.assertTrue(self._mlist.digestable) + self.assertTrue(self._mlist.digests_enabled) # Set up NNTP. self._mlist.gateway_to_news = True self._mlist.linked_newsgroup = 'testing' diff --git a/src/mailman/database/alembic/versions/70af5a4e5790_digests.py b/src/mailman/database/alembic/versions/70af5a4e5790_digests.py new file mode 100644 index 000000000..5df46b584 --- /dev/null +++ b/src/mailman/database/alembic/versions/70af5a4e5790_digests.py @@ -0,0 +1,27 @@ +"""digests + +Revision ID: 70af5a4e5790 +Revises: 47294d3a604 +Create Date: 2015-12-19 12:05:42.202998 + +""" + +# revision identifiers, used by Alembic. +revision = '70af5a4e5790' +down_revision = '47294d3a604' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + with op.batch_alter_table('mailinglist') as batch_op: + batch_op.alter_column('digestable', new_column_name='digests_enabled') + batch_op.drop_column('nondigestable') + + +def downgrade(): + with op.batch_alter_table('mailinglist') as batch_op: + batch_op.alter_column('digests_enabled', new_column_name='digestable') + # The data for this column is lost, it's not used anyway. + batch_op.add_column(sa.Column('nondigestable', sa.Boolean)) diff --git a/src/mailman/database/tests/test_migrations.py b/src/mailman/database/tests/test_migrations.py index 823c94652..e3f4b9581 100644 --- a/src/mailman/database/tests/test_migrations.py +++ b/src/mailman/database/tests/test_migrations.py @@ -169,3 +169,30 @@ class TestMigrations(unittest.TestCase): self.assertNotIn('type', results[i]) self.assertEqual(results[4]['type'], '"held message"') self.assertEqual(results[5]['type'], '"registration"') + + def test_70af5a4e5790_digests(self): + IDS_TO_DIGESTABLE = [ + (1, True), + (2, False), + (3, False), + (4, True), + ] + mlist_table = sa.sql.table( + 'mailinglist', + sa.sql.column('id', sa.Integer), + sa.sql.column('digests_enabled', sa.Boolean) + ) + # Downgrading. + for table_id, enabled in IDS_TO_DIGESTABLE: + config.db.store.execute(mlist_table.insert().values( + id=table_id, digests_enabled=enabled)) + config.db.store.commit() + alembic.command.downgrade(alembic_cfg, '47294d3a604') + results = config.db.store.execute( + 'SELECT id, digestable FROM mailinglist').fetchall() + self.assertEqual(results, IDS_TO_DIGESTABLE) + # Upgrading. + alembic.command.upgrade(alembic_cfg, '70af5a4e5790') + results = config.db.store.execute( + 'SELECT id, digests_enabled FROM mailinglist').fetchall() + self.assertEqual(results, IDS_TO_DIGESTABLE) diff --git a/src/mailman/handlers/docs/digests.rst b/src/mailman/handlers/docs/digests.rst index c3fc62ebf..828a6b2cc 100644 --- a/src/mailman/handlers/docs/digests.rst +++ b/src/mailman/handlers/docs/digests.rst @@ -37,7 +37,7 @@ Short circuiting When a message is posted to the mailing list, it is generally added to a mailbox, unless the mailing list does not allow digests. - >>> mlist.digestable = False + >>> mlist.digests_enabled = False >>> msg = next(message_factory) >>> process = config.handlers['to-digest'].process >>> process(mlist, msg, {}) @@ -49,7 +49,7 @@ mailbox, unless the mailing list does not allow digests. ...or they may allow digests but the message is already a digest. - >>> mlist.digestable = True + >>> mlist.digests_enabled = True >>> process(mlist, msg, dict(isdigest=True)) >>> sum(1 for msg in digest_mbox(mlist)) 0 diff --git a/src/mailman/handlers/to_digest.py b/src/mailman/handlers/to_digest.py index fa3f624be..89e1acaea 100644 --- a/src/mailman/handlers/to_digest.py +++ b/src/mailman/handlers/to_digest.py @@ -44,8 +44,9 @@ class ToDigest: def process(self, mlist, msg, msgdata): """See `IHandler`.""" - # Short circuit for non-digestable messages. - if not mlist.digestable or msgdata.get('isdigest'): + # Short-circuit if digests are not enabled or if this message already + # is a digest. + if not mlist.digests_enabled or msgdata.get('isdigest'): return # Open the mailbox that will be used to collect the current digest. mailbox_path = os.path.join(mlist.data_path, 'digest.mmdf') diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py index 0be8c2b68..75fdce327 100644 --- a/src/mailman/interfaces/mailinglist.py +++ b/src/mailman/interfaces/mailinglist.py @@ -308,6 +308,19 @@ class IMailingList(Interface): # Digests. + digests_enabled = Attribute( + """Whether or not digests are enabled for this mailing list.""") + + digest_size_threshold = Attribute( + """The maximum (approximate) size in kilobytes of the digest currently + being collected.""") + + digest_send_periodic = Attribute( + "Should a digest be sent daily even when the size threshold isn't met?") + + digest_volume_frequency = Attribute( + """How often should a new digest volume be started?""") + digest_last_sent_at = Attribute( """The date and time a digest of this mailing list was last sent.""") @@ -322,10 +335,6 @@ class IMailingList(Interface): the digest volume number is bumped, the digest number is reset to 1.""") - digest_size_threshold = Attribute( - """The maximum (approximate) size in kilobytes of the digest currently - being collected.""") - def send_one_last_digest_to(address, delivery_mode): """Make sure to send one last digest to an address. diff --git a/src/mailman/model/mailinglist.py b/src/mailman/model/mailinglist.py index 0a5b20dd8..9ee52629f 100644 --- a/src/mailman/model/mailinglist.py +++ b/src/mailman/model/mailinglist.py @@ -135,13 +135,13 @@ class MailingList(Model): default_member_action = Column(Enum(Action)) default_nonmember_action = Column(Enum(Action)) description = Column(Unicode) + digests_enabled = Column(Boolean) 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(DigestFrequency)) - digestable = Column(Boolean) discard_these_nonmembers = Column(PickleType) emergency = Column(Boolean) encode_ascii_prefixes = Column(Boolean) @@ -164,7 +164,6 @@ class MailingList(Model): moderator_password = Column(LargeBinary) # TODO : was RawStr() 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) diff --git a/src/mailman/rest/docs/listconf.rst b/src/mailman/rest/docs/listconf.rst index 319028be5..8a7e2c526 100644 --- a/src/mailman/rest/docs/listconf.rst +++ b/src/mailman/rest/docs/listconf.rst @@ -37,7 +37,10 @@ All readable attributes for a list are available on a sub-resource. default_nonmember_action: hold description: digest_last_sent_at: None + digest_send_periodic: True digest_size_threshold: 30.0 + digest_volume_frequency: monthly + digests_enabled: True display_name: Ant filter_content: False first_strip_reply_to: False @@ -97,7 +100,10 @@ When using ``PUT``, all writable attributes must be included. ... description='This is my mailing list', ... include_rfc2369_headers=False, ... allow_list_posts=False, + ... digest_send_periodic=False, ... digest_size_threshold=10.5, + ... digest_volume_frequency='yearly', + ... digests_enabled=False, ... posting_pipeline='virgin', ... filter_content=True, ... first_strip_reply_to=True, @@ -145,7 +151,10 @@ These values are changed permanently. default_nonmember_action: discard description: This is my mailing list ... + digest_send_periodic: False digest_size_threshold: 10.5 + digest_volume_frequency: yearly + digests_enabled: False display_name: Fnords filter_content: True first_strip_reply_to: True diff --git a/src/mailman/rest/listconf.py b/src/mailman/rest/listconf.py index 62b21644a..b85b32b14 100644 --- a/src/mailman/rest/listconf.py +++ b/src/mailman/rest/listconf.py @@ -29,6 +29,7 @@ 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.digests import DigestFrequency from mailman.interfaces.mailinglist import ( IAcceptableAliasSet, ReplyToMunging, SubscriptionPolicy) from mailman.rest.helpers import ( @@ -112,7 +113,10 @@ ATTRIBUTES = dict( default_nonmember_action=GetterSetter(enum_validator(Action)), description=GetterSetter(str), digest_last_sent_at=GetterSetter(None), + digest_send_periodic=GetterSetter(as_boolean), digest_size_threshold=GetterSetter(float), + digest_volume_frequency=GetterSetter(enum_validator(DigestFrequency)), + digests_enabled=GetterSetter(as_boolean), filter_content=GetterSetter(as_boolean), first_strip_reply_to=GetterSetter(as_boolean), fqdn_listname=GetterSetter(None), diff --git a/src/mailman/rest/tests/test_listconf.py b/src/mailman/rest/tests/test_listconf.py index 0552233bb..0cbf37b1b 100644 --- a/src/mailman/rest/tests/test_listconf.py +++ b/src/mailman/rest/tests/test_listconf.py @@ -26,6 +26,7 @@ import unittest from mailman.app.lifecycle import create_list from mailman.database.transaction import transaction +from mailman.interfaces.digests import DigestFrequency from mailman.interfaces.mailinglist import ( IAcceptableAliasSet, SubscriptionPolicy) from mailman.testing.helpers import call_api @@ -58,9 +59,10 @@ RESOURCE = dict( description='This is my mailing list', include_rfc2369_headers=False, allow_list_posts=False, - #digest_send_periodic='yes', + digest_send_periodic=True, digest_size_threshold=10.5, - #digest_volume_frequency=1, + digest_volume_frequency='monthly', + digests_enabled=True, posting_pipeline='virgin', filter_content=True, first_strip_reply_to=True, @@ -198,7 +200,7 @@ class TestConfiguration(unittest.TestCase): def test_patch_attribute_double(self): with self.assertRaises(HTTPError) as cm: - resource, response = call_api( + call_api( 'http://localhost:9001/3.0/lists/ant.example.com' '/config/reply_to_address', dict(display_name='bar@ant.example.com', @@ -255,3 +257,121 @@ class TestConfiguration(unittest.TestCase): self.assertEqual(cm.exception.code, 400) self.assertEqual(cm.exception.reason, b'Cannot convert parameters: posting_pipeline') + + def test_get_digest_send_periodic(self): + with transaction(): + self._mlist.digest_send_periodic = False + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_send_periodic') + self.assertFalse(resource['digest_send_periodic']) + + def test_patch_digest_send_periodic(self): + with transaction(): + self._mlist.digest_send_periodic = False + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_send_periodic', + dict(digest_send_periodic=True), + 'PATCH') + self.assertEqual(response.status, 204) + self.assertTrue(self._mlist.digest_send_periodic) + + def test_put_digest_send_periodic(self): + with transaction(): + self._mlist.digest_send_periodic = False + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_send_periodic', + dict(digest_send_periodic=True), + 'PUT') + self.assertEqual(response.status, 204) + self.assertTrue(self._mlist.digest_send_periodic) + + def test_get_digest_volume_frequency(self): + with transaction(): + self._mlist.digest_volume_frequency = DigestFrequency.yearly + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_volume_frequency') + self.assertEqual(resource['digest_volume_frequency'], 'yearly') + + def test_patch_digest_volume_frequency(self): + with transaction(): + self._mlist.digest_volume_frequency = DigestFrequency.yearly + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_volume_frequency', + dict(digest_volume_frequency='monthly'), + 'PATCH') + self.assertEqual(response.status, 204) + self.assertEqual(self._mlist.digest_volume_frequency, + DigestFrequency.monthly) + + def test_put_digest_volume_frequency(self): + with transaction(): + self._mlist.digest_volume_frequency = DigestFrequency.yearly + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_volume_frequency', + dict(digest_volume_frequency='monthly'), + 'PUT') + self.assertEqual(response.status, 204) + self.assertEqual(self._mlist.digest_volume_frequency, + DigestFrequency.monthly) + + def test_bad_patch_digest_volume_frequency(self): + with transaction(): + self._mlist.digest_volume_frequency = DigestFrequency.yearly + with self.assertRaises(HTTPError) as cm: + call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_volume_frequency', + dict(digest_volume_frequency='once in a while'), + 'PATCH') + self.assertEqual(cm.exception.code, 400) + self.assertEqual(cm.exception.reason, + b'Cannot convert parameters: digest_volume_frequency') + + def test_bad_put_digest_volume_frequency(self): + with transaction(): + self._mlist.digest_volume_frequency = DigestFrequency.yearly + with self.assertRaises(HTTPError) as cm: + call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digest_volume_frequency', + dict(digest_volume_frequency='once in a while'), + 'PUT') + self.assertEqual(cm.exception.code, 400) + self.assertEqual(cm.exception.reason, + b'Cannot convert parameters: digest_volume_frequency') + + def test_get_digests_enabled(self): + with transaction(): + self._mlist.digests_enabled = False + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digests_enabled') + self.assertFalse(resource['digests_enabled']) + + def test_patch_digests_enabled(self): + with transaction(): + self._mlist.digests_enabled = False + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digests_enabled', + dict(digests_enabled=True), + 'PATCH') + self.assertEqual(response.status, 204) + self.assertTrue(self._mlist.digests_enabled) + + def test_put_digests_enabled(self): + with transaction(): + self._mlist.digests_enabled = False + resource, response = call_api( + 'http://localhost:9001/3.0/lists/ant.example.com/config' + '/digests_enabled', + dict(digests_enabled=True), + 'PUT') + self.assertEqual(response.status, 204) + self.assertTrue(self._mlist.digests_enabled) diff --git a/src/mailman/styles/base.py b/src/mailman/styles/base.py index 7a77af609..4f9352fd3 100644 --- a/src/mailman/styles/base.py +++ b/src/mailman/styles/base.py @@ -87,7 +87,7 @@ class BasicOperation: mlist.filter_action = FilterAction.discard mlist.filter_content = False # Digests. - mlist.digestable = True + mlist.digests_enabled = True mlist.digest_is_default = False mlist.mime_is_default_digest = False mlist.digest_size_threshold = 30 # KB @@ -97,7 +97,6 @@ class BasicOperation: 'mailman:///$listname/$language/footer-generic.txt') mlist.digest_volume_frequency = DigestFrequency.monthly mlist.next_digest_number = 1 - mlist.nondigestable = True # NNTP gateway mlist.nntp_host = '' mlist.linked_newsgroup = '' |
