diff options
| -rw-r--r-- | src/mailman/database/address.py | 8 | ||||
| -rw-r--r-- | src/mailman/database/digests.py | 53 | ||||
| -rw-r--r-- | src/mailman/database/mailinglist.py | 21 | ||||
| -rw-r--r-- | src/mailman/database/mailman.sql | 12 | ||||
| -rw-r--r-- | src/mailman/database/roster.py | 59 | ||||
| -rw-r--r-- | src/mailman/docs/addresses.txt | 3 | ||||
| -rw-r--r-- | src/mailman/docs/membership.txt | 2 | ||||
| -rw-r--r-- | src/mailman/interfaces/digests.py | 42 | ||||
| -rw-r--r-- | src/mailman/interfaces/mailinglist.py | 31 | ||||
| -rw-r--r-- | src/mailman/queue/digest.py | 18 | ||||
| -rw-r--r-- | src/mailman/queue/docs/digester.txt | 195 | ||||
| -rw-r--r-- | src/mailman/styles/default.py | 1 |
12 files changed, 349 insertions, 96 deletions
diff --git a/src/mailman/database/address.py b/src/mailman/database/address.py index 528d3af51..f63f03e14 100644 --- a/src/mailman/database/address.py +++ b/src/mailman/database/address.py @@ -26,10 +26,9 @@ __all__ = [ from email.utils import formataddr -from storm.locals import * +from storm.locals import DateTime, Int, Reference, Store, Unicode from zope.interface import implements -from mailman.config import config from mailman.database.member import Member from mailman.database.model import Model from mailman.database.preferences import Preferences @@ -76,7 +75,8 @@ class Address(Model): def subscribe(self, mailing_list, role): # This member has no preferences by default. - member = config.db.store.find( + store = Store.of(self) + member = store.find( Member, Member.role == role, Member.mailing_list == mailing_list.fqdn_listname, @@ -88,7 +88,7 @@ class Address(Model): mailing_list=mailing_list.fqdn_listname, address=self) member.preferences = Preferences() - config.db.store.add(member) + store.add(member) return member @property diff --git a/src/mailman/database/digests.py b/src/mailman/database/digests.py new file mode 100644 index 000000000..291dafa28 --- /dev/null +++ b/src/mailman/database/digests.py @@ -0,0 +1,53 @@ +# Copyright (C) 2009 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/>. + +"""One last digest.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'OneLastDigest', + ] + + +from storm.locals import Int, Reference +from zope.interface import implements + +from mailman.database.model import Model +from mailman.database.types import Enum +from mailman.interfaces.digests import IOneLastDigest + + + +class OneLastDigest(Model): + implements(IOneLastDigest) + + id = Int(primary=True) + + mailing_list_id = Int() + mailing_list = Reference(mailing_list_id, 'MailingList.id') + + address_id = Int() + address = Reference(address_id, 'Address.id') + + delivery_mode = Enum() + + def __init__(self, mailing_list, address, delivery_mode): + self.mailing_list = mailing_list + self.address = address + self.delivery_mode = delivery_mode diff --git a/src/mailman/database/mailinglist.py b/src/mailman/database/mailinglist.py index 9141ad64c..2956cac57 100644 --- a/src/mailman/database/mailinglist.py +++ b/src/mailman/database/mailinglist.py @@ -28,12 +28,14 @@ __all__ = [ import os import string -from storm.locals import * +from storm.locals import ( + Bool, DateTime, Float, Int, Pickle, Store, TimeDelta, Unicode) from urlparse import urljoin from zope.interface import implements from mailman.config import config from mailman.database import roster +from mailman.database.digests import OneLastDigest from mailman.database.model import Model from mailman.database.types import Enum from mailman.interfaces.mailinglist import IMailingList, Personalization @@ -63,7 +65,6 @@ class MailingList(Model): next_request_id = Int() next_digest_number = Int() digest_last_sent_at = DateTime() - one_last_digest = Pickle() volume = Int() last_post_time = DateTime() # Attributes which are directly modifiable via the web u/i. The more @@ -276,3 +277,19 @@ class MailingList(Model): self._preferred_language = language.code except AttributeError: self._preferred_language = language + + def send_one_last_digest_to(self, address, delivery_mode): + """See `IMailingList`.""" + digest = OneLastDigest(self, address, delivery_mode) + Store.of(self).add(digest) + + @property + def last_digest_recipients(self): + """See `IMailingList`.""" + results = Store.of(self).find( + OneLastDigest, + OneLastDigest.mailing_list == self) + recipients = [(digest.address, digest.delivery_mode) + for digest in results] + results.remove() + return recipients diff --git a/src/mailman/database/mailman.sql b/src/mailman/database/mailman.sql index c46fb193e..79a28574e 100644 --- a/src/mailman/database/mailman.sql +++ b/src/mailman/database/mailman.sql @@ -54,7 +54,6 @@ CREATE TABLE mailinglist ( next_request_id INTEGER, next_digest_number INTEGER, digest_last_sent_at TIMESTAMP, - one_last_digest BLOB, volume INTEGER, last_post_time TIMESTAMP, accept_these_nonmembers BLOB, @@ -178,6 +177,17 @@ CREATE TABLE message ( message_id TEXT, PRIMARY KEY (id) ); +CREATE TABLE onelastdigest ( + id INTEGER NOT NULL, + mailing_list_id INTEGER, + address_id INTEGER, + delivery_mode TEXT, + 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, diff --git a/src/mailman/database/roster.py b/src/mailman/database/roster.py index fc0a24c7d..3b8ba4c4c 100644 --- a/src/mailman/database/roster.py +++ b/src/mailman/database/roster.py @@ -37,12 +37,13 @@ __all__ = [ ] -from storm.locals import * +from storm.expr import And, LeftJoin, Or from zope.interface import implements from mailman.config import config from mailman.database.address import Address from mailman.database.member import Member +from mailman.database.preferences import Preferences from mailman.interfaces.member import DeliveryMode, MemberRole from mailman.interfaces.roster import IRoster @@ -167,49 +168,49 @@ class AdministratorRoster(AbstractRoster): -class RegularMemberRoster(AbstractRoster): +class DeliveryMemberRoster(AbstractRoster): + """Return all the members having a particular kind of delivery.""" + + def _get_members(self, *delivery_modes): + """The set of members for a mailing list, filter by delivery mode. + + :param delivery_modes: The modes to filter on. + :type delivery_modes: sequence of `DeliveryMode`. + :return: A generator of members. + :rtype: generator + """ + results = config.db.store.find( + Member, + And(Member.mailing_list == self._mlist.fqdn_listname, + Member.role == MemberRole.member)) + for member in results: + if member.delivery_mode in delivery_modes: + yield member + + +class RegularMemberRoster(DeliveryMemberRoster): """Return all the regular delivery members of a list.""" name = 'regular_members' @property def members(self): - # Query for all the Members which have a role of MemberRole.member and - # are subscribed to this mailing list. Then return only those members - # that have a regular delivery mode. - for member in config.db.store.find( - Member, - mailing_list=self._mlist.fqdn_listname, - role=MemberRole.member): - if member.delivery_mode == DeliveryMode.regular: - yield member + for member in self._get_members(DeliveryMode.regular): + yield member -_digest_modes = ( - DeliveryMode.mime_digests, - DeliveryMode.plaintext_digests, - DeliveryMode.summary_digests, - ) - - - -class DigestMemberRoster(AbstractRoster): +class DigestMemberRoster(DeliveryMemberRoster): """Return all the regular delivery members of a list.""" name = 'digest_members' @property def members(self): - # Query for all the Members which have a role of MemberRole.member and - # are subscribed to this mailing list. Then return only those members - # that have one of the digest delivery modes. - for member in config.db.store.find( - Member, - mailing_list=self._mlist.fqdn_listname, - role=MemberRole.member): - if member.delivery_mode in _digest_modes: - yield member + for member in self._get_members(DeliveryMode.plaintext_digests, + DeliveryMode.mime_digests, + DeliveryMode.summary_digests): + yield member diff --git a/src/mailman/docs/addresses.txt b/src/mailman/docs/addresses.txt index 9eccb2673..a8ae24840 100644 --- a/src/mailman/docs/addresses.txt +++ b/src/mailman/docs/addresses.txt @@ -148,7 +148,8 @@ subscribed, a role is specified. >>> address_5 = usermgr.create_address( ... u'eperson@example.com', u'Elly Person') - >>> mlist = config.db.list_manager.create(u'_xtext@example.com') + >>> mlist = create_list(u'_xtext@example.com') + >>> from mailman.interfaces.member import MemberRole >>> address_5.subscribe(mlist, MemberRole.owner) <Member: Elly Person <eperson@example.com> on diff --git a/src/mailman/docs/membership.txt b/src/mailman/docs/membership.txt index 7f9f16738..65f5b37ed 100644 --- a/src/mailman/docs/membership.txt +++ b/src/mailman/docs/membership.txt @@ -2,7 +2,7 @@ List memberships ================ Users represent people in Mailman. Users control email addresses, and rosters -are collectons of members. A member gives an email address a role, such as +are collections of members. A member gives an email address a role, such as 'member', 'administrator', or 'moderator'. Roster sets are collections of rosters and a mailing list has a single roster set that contains all its members, regardless of that member's role. diff --git a/src/mailman/interfaces/digests.py b/src/mailman/interfaces/digests.py new file mode 100644 index 000000000..009be8e70 --- /dev/null +++ b/src/mailman/interfaces/digests.py @@ -0,0 +1,42 @@ +# Copyright (C) 2009 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/>. + +"""One last digest.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'IOneLastDigest' + ] + + +from zope.interface import Interface, Attribute + + + +class IOneLastDigest(Interface): + """Users who should receive one last digest.""" + + mailing_list = Attribute( + """The mailing list for the one last digest.""") + + address = Attribute( + """The address to receive the one last digest.""") + + delivery_mode = Attribute( + """The digest delivery mode to send.""") diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py index 2885f60ab..4e04fa39b 100644 --- a/src/mailman/interfaces/mailinglist.py +++ b/src/mailman/interfaces/mailinglist.py @@ -216,25 +216,30 @@ class IMailingList(Interface): the digest volume number is bumped, the digest number is reset to 1.""") - message_count = Attribute( - """The number of messages in the digest currently being collected.""") - digest_size_threshold = Attribute( """The maximum (approximate) size in kilobytes of the digest currently being collected.""") - messages = Attribute( - """An iterator over all the messages in the digest currently being - created. Returns individual IPostedMessage objects. - """) + def send_one_last_digest_to(address, delivery_mode): + """Make sure to send one last digest to an address. + + This is used when a person transitions from digest delivery to regular + delivery and wants to make sure they don't miss anything. By + indicating that they'd like to receive one last digest, they will + ensure continuity in receiving mailing lists posts. - limits = Attribute( - """An iterator over the IDigestLimiters associated with this digest. - Each limiter can make a determination of whether the digest has - reached the threshold for being automatically sent.""") + :param address: The address of the person receiving one last digest. + :type address: `IAddress` + :param delivery_mode: The type of digest to receive. + :type delivery_mode: `DeliveryMode` + """ + + last_digest_recipients = Attribute( + """An iterator over the addresses that should receive one last digest. - def send(): - """Send this digest now.""" + Items are 2-tuples of (`IAddress`, `DeliveryMode`). The one last + digest recipients are cleared. + """) decorators = Attribute( """An iterator over all the IDecorators associated with this digest. diff --git a/src/mailman/queue/digest.py b/src/mailman/queue/digest.py index a590a9997..30705b8be 100644 --- a/src/mailman/queue/digest.py +++ b/src/mailman/queue/digest.py @@ -43,6 +43,7 @@ from mailman.Utils import maketext, oneline, wrap from mailman.config import config from mailman.core.errors import DiscardMessage from mailman.i18n import _ +from mailman.interfaces.member import DeliveryMode, DeliveryStatus from mailman.pipeline.decorate import decorate from mailman.pipeline.scrubber import process as scrubber from mailman.queue import Runner @@ -332,10 +333,6 @@ class DigestRunner(Runner): # digest to ensure that there will be no gaps in the messages they # receive. digest_members = set(mlist.digest_members.members) - for address in mlist.one_last_digest: - member = mlist.digest_members.get_member(address) - if member: - digest_members.add(member) for member in digest_members: if member.delivery_status <> DeliveryStatus.enabled: continue @@ -350,6 +347,16 @@ class DigestRunner(Runner): raise AssertionError( 'Digest member "{0}" unexpected delivery mode: {1}'.format( email_address, member.delivery_mode)) + # Add also the folks who are receiving one last digest. + for address, delivery_mode in mlist.last_digest_recipients: + if delivery_mode == DeliveryMode.plaintext_digests: + rfc1153_recipients.add(address.original_address) + elif delivery_mode == DeliveryMode.mime_digests: + mime_recipients.add(address.original_address) + else: + raise AssertionError( + 'OLD recipient "{0}" unexpected delivery mode: {1}'.format( + address, delivery_mode)) # Send the digests to the virgin queue for final delivery. queue = config.switchboards['virgin'] queue.enqueue(mime, @@ -360,6 +367,3 @@ class DigestRunner(Runner): recips=rfc1153_recipients, listname=mlist.fqdn_listname, isdigest=True) - # Now that we've delivered the last digest to folks who were waiting - # for it, clear that recipient set. - mlist.one_last_digest.clear() diff --git a/src/mailman/queue/docs/digester.txt b/src/mailman/queue/docs/digester.txt index 57e80317c..2c9c813b2 100644 --- a/src/mailman/queue/docs/digester.txt +++ b/src/mailman/queue/docs/digester.txt @@ -8,23 +8,27 @@ This starts by a number of messages being posted to the mailing list. >>> mlist.digest_size_threshold = 0.5 >>> mlist.volume = 1 >>> mlist.next_digest_number = 1 - >>> size = 0 >>> from string import Template >>> process = config.handlers['to-digest'].process - >>> for i in range(1, 5): - ... text = Template("""\ + + >>> def fill_digest(): + ... size = 0 + ... for i in range(1, 5): + ... text = Template("""\ ... From: aperson@example.com ... To: xtest@example.com ... Subject: Test message $i ... ... Here is message $i ... """).substitute(i=i) - ... msg = message_from_string(text) - ... process(mlist, msg, {}) - ... size += len(text) - ... if size >= mlist.digest_size_threshold * 1024: - ... break + ... msg = message_from_string(text) + ... process(mlist, msg, {}) + ... size += len(text) + ... if size >= mlist.digest_size_threshold * 1024: + ... break + + >>> fill_digest() The queue runner gets kicked off when a marker message gets dropped into the digest queue. The message metadata points to the mailbox file containing the @@ -77,16 +81,16 @@ delivery. The MIME digest is a multipart, and the RFC 1153 digest is the other one. - >>> if messages[0].msg.is_multipart(): - ... mime = messages[0].msg - ... rfc1153 = messages[1].msg - ... else: - ... mime = messages[1].msg - ... rfc1153 = messages[0].msg + >>> def mime_rfc1153(messages): + ... if messages[0].msg.is_multipart(): + ... return messages[0], messages[1] + ... return messages[1], messages[0] + + >>> mime, rfc1153 = mime_rfc1153(messages) The MIME digest has lots of good stuff, all contained in the multipart. - >>> print mime.as_string() + >>> print mime.msg.as_string() Content-Type: multipart/mixed; boundary="===============...==" MIME-Version: 1.0 From: test-request@example.com @@ -103,15 +107,15 @@ The MIME digest has lots of good stuff, all contained in the multipart. Content-Description: Test Digest, Vol 1, Issue 1 <BLANKLINE> Send Test mailing list submissions to - test@example.com + test@example.com <BLANKLINE> To subscribe or unsubscribe via the World Wide Web, visit - http://lists.example.com/listinfo/test@example.com + http://lists.example.com/listinfo/test@example.com or, via email, send a message with subject or body 'help' to - test-request@example.com + test-request@example.com <BLANKLINE> You can reach the person managing the list at - test-owner@example.com + test-owner@example.com <BLANKLINE> When replying, please edit your Subject line so it is more specific than "Re: Contents of Test digest..." @@ -184,7 +188,7 @@ The MIME digest has lots of good stuff, all contained in the multipart. The RFC 1153 contains the digest in a single plain text message. - >>> print rfc1153.as_string() + >>> print rfc1153.msg.as_string() From: test-request@example.com Subject: Test Digest, Vol 1, Issue 1 To: test@example.com @@ -196,15 +200,15 @@ The RFC 1153 contains the digest in a single plain text message. Content-Transfer-Encoding: 7bit <BLANKLINE> Send Test mailing list submissions to - test@example.com + test@example.com <BLANKLINE> To subscribe or unsubscribe via the World Wide Web, visit - http://lists.example.com/listinfo/test@example.com + http://lists.example.com/listinfo/test@example.com or, via email, send a message with subject or body 'help' to - test-request@example.com + test-request@example.com <BLANKLINE> You can reach the person managing the list at - test-owner@example.com + test-owner@example.com <BLANKLINE> When replying, please edit your Subject line so it is more specific than "Re: Contents of Test digest..." @@ -324,16 +328,11 @@ queue. One of which is the MIME digest and the other of which is the RFC 1153 digest. - >>> if messages[0].msg.is_multipart(): - ... mime = messages[0].msg - ... rfc1153 = messages[1].msg - ... else: - ... mime = messages[1].msg - ... rfc1153 = messages[0].msg + >>> mime, rfc1153 = mime_rfc1153(messages) You can see that the digests contain a mix of French and Japanese. - >>> print mime.as_string() + >>> print mime.msg.as_string() Content-Type: multipart/mixed; boundary="===============...==" MIME-Version: 1.0 From: test-request@example.com @@ -350,17 +349,17 @@ You can see that the digests contain a mix of French and Japanese. Content-Description: Groupe Test, Vol. 1, Parution 2 <BLANKLINE> Envoyez vos messages pour la liste Test =E0 - test@example.com + test@example.com <BLANKLINE> Pour vous (d=E9s)abonner par le web, consultez - http://lists.example.com/listinfo/test@example.com + http://lists.example.com/listinfo/test@example.com <BLANKLINE> ou, par courriel, envoyez un message avec =AB=A0help=A0=BB dans le corps ou dans le sujet =E0 - test-request@example.com + test-request@example.com <BLANKLINE> Vous pouvez contacter l'administrateur de la liste =E0 l'adresse - test-owner@example.com + test-owner@example.com <BLANKLINE> Si vous r=E9pondez, n'oubliez pas de changer l'objet du message afin qu'il soit plus sp=E9cifique que =AB=A0Re: Contenu du groupe de Test...=A0= @@ -402,7 +401,7 @@ You can see that the digests contain a mix of French and Japanese. The RFC 1153 digest will be encoded in UTF-8 since it contains a mixture of French and Japanese characters. - >>> print rfc1153.as_string() + >>> print rfc1153.msg.as_string() From: test-request@example.com Subject: Groupe Test, Vol. 1, Parution 2 To: test@example.com @@ -420,7 +419,8 @@ The content can be decoded to see the actual digest text. # We must display the repr of the decoded value because doctests cannot # handle the non-ascii characters. - >>> [repr(line) for line in rfc1153.get_payload(decode=True).splitlines()] + >>> [repr(line) + ... for line in rfc1153.msg.get_payload(decode=True).splitlines()] ["'Envoyez vos messages pour la liste Test \\xc3\\xa0'", "'\\ttest@example.com'", "''", @@ -462,3 +462,124 @@ The content can be decoded to see the actual digest text. "''", "'Fin de Groupe Test, Vol. 1, Parution 2'", "'**************************************'"] + + >>> config.pop('french') + + +Digest delivery +--------------- + +A mailing list's members can choose to receive normal delivery, plain text +digests, or MIME digests. + + >>> len(get_queue_messages('virgin')) + 0 + + >>> from mailman.interfaces.member import DeliveryMode, MemberRole + >>> def subscribe(email, mode): + ... address = config.db.user_manager.create_address(email) + ... member = address.subscribe(mlist, MemberRole.member) + ... member.preferences.delivery_mode = mode + ... return member + +Two regular delivery members subscribe to the mailing list. + + >>> member_1 = subscribe(u'uperson@example.com', DeliveryMode.regular) + >>> member_2 = subscribe(u'vperson@example.com', DeliveryMode.regular) + +Two MIME digest members subscribe to the mailing list. + + >>> member_3 = subscribe(u'wperson@example.com', DeliveryMode.mime_digests) + >>> member_4 = subscribe(u'xperson@example.com', DeliveryMode.mime_digests) + +One RFC 1153 digest member subscribes to the mailing list. + + >>> member_5 = subscribe( + ... u'yperson@example.com', DeliveryMode.plaintext_digests) + >>> member_6 = subscribe( + ... u'zperson@example.com', DeliveryMode.plaintext_digests) + +When a digest gets sent, the appropriate recipient list is chosen. + + >>> mlist.preferred_language = u'en' + >>> mlist.digest_size_threshold = 0.5 + >>> fill_digest() + >>> runner.run() + +The digests are sitting in the virgin queue. One of them is the MIME digest +and the other is the RFC 1153 digest. + + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 2 + + >>> mime, rfc1153 = mime_rfc1153(messages) + +Only wperson and xperson get the MIME digests. + + >>> sorted(mime.msgdata['recips']) + [u'wperson@example.com', u'xperson@example.com'] + +Only yperson and zperson get the RFC 1153 digests. + + >>> sorted(rfc1153.msgdata['recips']) + [u'yperson@example.com', u'zperson@example.com'] + +Now uperson decides that they would like to start receiving digests too. + + >>> member_1.preferences.delivery_mode = DeliveryMode.mime_digests + >>> fill_digest() + >>> runner.run() + + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 2 + + >>> mime, rfc1153 = mime_rfc1153(messages) + >>> sorted(mime.msgdata['recips']) + [u'uperson@example.com', u'wperson@example.com', u'xperson@example.com'] + + >>> sorted(rfc1153.msgdata['recips']) + [u'yperson@example.com', u'zperson@example.com'] + +At this point, both uperson and wperson decide that they'd rather receive +regular deliveries instead of digests. uperson would like to get any last +digest that may be sent so that she doesn't miss anything. wperson does care +as much and does not want to receive one last digest. + + >>> mlist.send_one_last_digest_to( + ... member_1.address, member_1.preferences.delivery_mode) + + >>> member_1.preferences.delivery_mode = DeliveryMode.regular + >>> member_3.preferences.delivery_mode = DeliveryMode.regular + + >>> fill_digest() + >>> runner.run() + + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 2 + + >>> mime, rfc1153 = mime_rfc1153(messages) + >>> sorted(mime.msgdata['recips']) + [u'uperson@example.com', u'xperson@example.com'] + + >>> sorted(rfc1153.msgdata['recips']) + [u'yperson@example.com', u'zperson@example.com'] + +Since uperson has received their last digest, they will not get any more of +them. + + >>> fill_digest() + >>> runner.run() + + >>> messages = get_queue_messages('virgin') + >>> len(messages) + 2 + + >>> mime, rfc1153 = mime_rfc1153(messages) + >>> sorted(mime.msgdata['recips']) + [u'xperson@example.com'] + + >>> sorted(rfc1153.msgdata['recips']) + [u'yperson@example.com', u'zperson@example.com'] diff --git a/src/mailman/styles/default.py b/src/mailman/styles/default.py index 842517c9f..adea20582 100644 --- a/src/mailman/styles/default.py +++ b/src/mailman/styles/default.py @@ -123,7 +123,6 @@ $fqdn_listname ${listinfo_page} """ mlist.digest_volume_frequency = DigestFrequency.monthly - mlist.one_last_digest = {} mlist.next_digest_number = 1 mlist.nondigestable = True mlist.personalize = Personalization.none |
