diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/app/docs/moderator.rst | 77 | ||||
| -rw-r--r-- | src/mailman/commands/docs/withlist.rst | 4 | ||||
| -rw-r--r-- | src/mailman/database/base.py | 6 | ||||
| -rw-r--r-- | src/mailman/interfaces/domain.py | 2 | ||||
| -rw-r--r-- | src/mailman/interfaces/listmanager.py | 6 | ||||
| -rw-r--r-- | src/mailman/model/docs/mailinglist.rst | 15 | ||||
| -rw-r--r-- | src/mailman/model/domain.py | 4 | ||||
| -rw-r--r-- | src/mailman/model/listmanager.py | 3 | ||||
| -rw-r--r-- | src/mailman/rest/docs/moderation.rst | 32 | ||||
| -rw-r--r-- | src/mailman/testing/layers.py | 7 | ||||
| -rw-r--r-- | src/mailman/testing/testing.cfg | 6 |
11 files changed, 83 insertions, 79 deletions
diff --git a/src/mailman/app/docs/moderator.rst b/src/mailman/app/docs/moderator.rst index ee9df8eb5..490c9630a 100644 --- a/src/mailman/app/docs/moderator.rst +++ b/src/mailman/app/docs/moderator.rst @@ -211,9 +211,9 @@ moderators. ... ... Here's something important about our mailing list. ... """) - >>> hold_message(mlist, msg, {}, 'Needs approval') - 2 - >>> handle_message(mlist, 2, Action.discard, forward=['zack@example.com']) + >>> req_id = hold_message(mlist, msg, {}, 'Needs approval') + >>> handle_message(mlist, req_id, Action.discard, + ... forward=['zack@example.com']) The forwarded message is in the virgin queue, destined for the moderator. :: @@ -243,10 +243,9 @@ choosing and their preferred language. >>> from mailman.app.moderator import hold_subscription >>> from mailman.interfaces.member import DeliveryMode - >>> hold_subscription(mlist, - ... 'fred@example.org', 'Fred Person', + >>> req_id = hold_subscription( + ... mlist, 'fred@example.org', 'Fred Person', ... '{NONE}abcxyz', DeliveryMode.regular, 'en') - 2 Disposing of membership change requests @@ -257,26 +256,27 @@ dispositions for this membership change request. The most trivial is to simply defer a decision for now. >>> from mailman.app.moderator import handle_subscription - >>> handle_subscription(mlist, 2, Action.defer) - >>> requests.get_request(2) is not None + >>> handle_subscription(mlist, req_id, Action.defer) + >>> requests.get_request(req_id) is not None True The held subscription can also be discarded. - >>> handle_subscription(mlist, 2, Action.discard) - >>> print(requests.get_request(2)) + >>> handle_subscription(mlist, req_id, Action.discard) + >>> print(requests.get_request(req_id)) None Gwen tries to subscribe to the mailing list, but... - >>> hold_subscription(mlist, - ... 'gwen@example.org', 'Gwen Person', + >>> req_id = hold_subscription( + ... mlist, 'gwen@example.org', 'Gwen Person', ... '{NONE}zyxcba', DeliveryMode.regular, 'en') - 2 + ...her request is rejected... - >>> handle_subscription(mlist, 2, Action.reject, 'This is a closed list') + >>> handle_subscription( + ... mlist, req_id, Action.reject, 'This is a closed list') >>> messages = get_queue_messages('virgin') >>> len(messages) 1 @@ -304,14 +304,13 @@ The subscription can also be accepted. This subscribes the address to the mailing list. >>> mlist.send_welcome_message = False - >>> hold_subscription(mlist, - ... 'herb@example.org', 'Herb Person', + >>> req_id = hold_subscription( + ... mlist, 'herb@example.org', 'Herb Person', ... 'abcxyz', DeliveryMode.regular, 'en') - 2 The moderators accept the subscription request. - >>> handle_subscription(mlist, 2, Action.accept) + >>> handle_subscription(mlist, req_id, Action.accept) And now Herb is a member of the mailing list. @@ -328,29 +327,27 @@ the unsubscribing address is required. Herb now wants to leave the mailing list, but his request must be approved. >>> from mailman.app.moderator import hold_unsubscription - >>> hold_unsubscription(mlist, 'herb@example.org') - 2 + >>> req_id = hold_unsubscription(mlist, 'herb@example.org') As with subscription requests, the unsubscription request can be deferred. >>> from mailman.app.moderator import handle_unsubscription - >>> handle_unsubscription(mlist, 2, Action.defer) + >>> handle_unsubscription(mlist, req_id, Action.defer) >>> print(mlist.members.get_member('herb@example.org').address) Herb Person <herb@example.org> The held unsubscription can also be discarded, and the member will remain subscribed. - >>> handle_unsubscription(mlist, 2, Action.discard) + >>> handle_unsubscription(mlist, req_id, Action.discard) >>> print(mlist.members.get_member('herb@example.org').address) Herb Person <herb@example.org> The request can be rejected, in which case a message is sent to the member, and the person remains a member of the mailing list. - >>> hold_unsubscription(mlist, 'herb@example.org') - 2 - >>> handle_unsubscription(mlist, 2, Action.reject, 'No can do') + >>> req_id = hold_unsubscription(mlist, 'herb@example.org') + >>> handle_unsubscription(mlist, req_id, Action.reject, 'No can do') >>> print(mlist.members.get_member('herb@example.org').address) Herb Person <herb@example.org> @@ -381,10 +378,9 @@ Herb gets a rejection notice. The unsubscription request can also be accepted. This removes the member from the mailing list. - >>> hold_unsubscription(mlist, 'herb@example.org') - 2 + >>> req_id = hold_unsubscription(mlist, 'herb@example.org') >>> mlist.send_goodbye_message = False - >>> handle_unsubscription(mlist, 2, Action.accept) + >>> handle_unsubscription(mlist, req_id, Action.accept) >>> print(mlist.members.get_member('herb@example.org')) None @@ -403,9 +399,8 @@ list is configured to send them. Iris tries to subscribe to the mailing list. - >>> hold_subscription(mlist, 'iris@example.org', 'Iris Person', + >>> req_id = hold_subscription(mlist, 'iris@example.org', 'Iris Person', ... 'password', DeliveryMode.regular, 'en') - 2 There's now a message in the virgin queue, destined for the list owner. @@ -429,8 +424,7 @@ There's now a message in the virgin queue, destined for the list owner. Similarly, the administrator gets notifications on unsubscription requests. Jeff is a member of the mailing list, and chooses to unsubscribe. - >>> hold_unsubscription(mlist, 'jeff@example.org') - 3 + >>> unsub_req_id = hold_unsubscription(mlist, 'jeff@example.org') >>> messages = get_queue_messages('virgin') >>> len(messages) 1 @@ -457,7 +451,7 @@ receive a membership change notice. >>> mlist.admin_notify_mchanges = True >>> mlist.admin_immed_notify = False - >>> handle_subscription(mlist, 2, Action.accept) + >>> handle_subscription(mlist, req_id, Action.accept) >>> messages = get_queue_messages('virgin') >>> len(messages) 1 @@ -474,9 +468,8 @@ receive a membership change notice. Similarly when an unsubscription request is accepted, the administrators can get a notification. - >>> hold_unsubscription(mlist, 'iris@example.org') - 4 - >>> handle_unsubscription(mlist, 4, Action.accept) + >>> req_id = hold_unsubscription(mlist, 'iris@example.org') + >>> handle_unsubscription(mlist, req_id, Action.accept) >>> messages = get_queue_messages('virgin') >>> len(messages) 1 @@ -498,10 +491,9 @@ can get a welcome message. >>> mlist.admin_notify_mchanges = False >>> mlist.send_welcome_message = True - >>> hold_subscription(mlist, 'kate@example.org', 'Kate Person', - ... 'password', DeliveryMode.regular, 'en') - 4 - >>> handle_subscription(mlist, 4, Action.accept) + >>> req_id = hold_subscription(mlist, 'kate@example.org', 'Kate Person', + ... 'password', DeliveryMode.regular, 'en') + >>> handle_subscription(mlist, req_id, Action.accept) >>> messages = get_queue_messages('virgin') >>> len(messages) 1 @@ -523,9 +515,8 @@ Similarly, when the member's unsubscription request is approved, she'll get a goodbye message. >>> mlist.send_goodbye_message = True - >>> hold_unsubscription(mlist, 'kate@example.org') - 4 - >>> handle_unsubscription(mlist, 4, Action.accept) + >>> req_id = hold_unsubscription(mlist, 'kate@example.org') + >>> handle_unsubscription(mlist, req_id, Action.accept) >>> messages = get_queue_messages('virgin') >>> len(messages) 1 diff --git a/src/mailman/commands/docs/withlist.rst b/src/mailman/commands/docs/withlist.rst index 827d246cd..e915eb04c 100644 --- a/src/mailman/commands/docs/withlist.rst +++ b/src/mailman/commands/docs/withlist.rst @@ -90,13 +90,13 @@ must start with a caret. >>> args.listname = '^.*example.com' >>> command.process(args) The list's display name is Aardvark - The list's display name is Badger The list's display name is Badboys + The list's display name is Badger >>> args.listname = '^bad.*' >>> command.process(args) - The list's display name is Badger The list's display name is Badboys + The list's display name is Badger >>> args.listname = '^foo' >>> command.process(args) diff --git a/src/mailman/database/base.py b/src/mailman/database/base.py index e360dcedf..beb9c260d 100644 --- a/src/mailman/database/base.py +++ b/src/mailman/database/base.py @@ -114,3 +114,9 @@ class SABaseDatabase: session = sessionmaker(bind=self.engine) self.store = session() self.store.commit() + + # XXX BAW Why doesn't model.py _reset() do this? + def destroy(self): + """Drop all database tables""" + from mailman.database.model import Model + Model.metadata.drop_all(self.engine) diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py index e8610fd76..e7cb0d901 100644 --- a/src/mailman/interfaces/domain.py +++ b/src/mailman/interfaces/domain.py @@ -166,6 +166,8 @@ class IDomainManager(Interface): def __iter__(): """An iterator over all the domains. + Domains are returned sorted by `mail_host`. + :return: iterator over `IDomain`. """ diff --git a/src/mailman/interfaces/listmanager.py b/src/mailman/interfaces/listmanager.py index 22d7b3418..0c641fb91 100644 --- a/src/mailman/interfaces/listmanager.py +++ b/src/mailman/interfaces/listmanager.py @@ -130,8 +130,10 @@ class IListManager(Interface): """ mailing_lists = Attribute( - """An iterator over all the mailing list objects managed by this list - manager.""") + """An iterator over all the mailing list objects. + + The mailing lists are returned in order sorted by `list_id`. + """) def __iter__(): """An iterator over all the mailing lists. diff --git a/src/mailman/model/docs/mailinglist.rst b/src/mailman/model/docs/mailinglist.rst index 53ba99575..3d01710c5 100644 --- a/src/mailman/model/docs/mailinglist.rst +++ b/src/mailman/model/docs/mailinglist.rst @@ -50,7 +50,10 @@ receive a copy of any message sent to the mailing list. Both addresses appear on the roster of members. - >>> for member in mlist.members.members: + >>> from operator import attrgetter + >>> sort_key = attrgetter('address.email') + + >>> for member in sorted(mlist.members.members, key=sort_key): ... print(member) <Member: aperson@example.com on aardvark@example.com as MemberRole.member> <Member: bperson@example.com on aardvark@example.com as MemberRole.member> @@ -72,7 +75,7 @@ A Person is now both a member and an owner of the mailing list. C Person is an owner and a moderator. :: - >>> for member in mlist.owners.members: + >>> for member in sorted(mlist.owners.members, key=sort_key): ... print(member) <Member: aperson@example.com on aardvark@example.com as MemberRole.owner> <Member: cperson@example.com on aardvark@example.com as MemberRole.owner> @@ -87,13 +90,13 @@ All rosters can also be accessed indirectly. :: >>> roster = mlist.get_roster(MemberRole.member) - >>> for member in roster.members: + >>> for member in sorted(roster.members, key=sort_key): ... print(member) <Member: aperson@example.com on aardvark@example.com as MemberRole.member> <Member: bperson@example.com on aardvark@example.com as MemberRole.member> >>> roster = mlist.get_roster(MemberRole.owner) - >>> for member in roster.members: + >>> for member in sorted(roster.members, key=sort_key): ... print(member) <Member: aperson@example.com on aardvark@example.com as MemberRole.owner> <Member: cperson@example.com on aardvark@example.com as MemberRole.owner> @@ -122,7 +125,7 @@ just by changing their preferred address. >>> mlist.subscribe(user) <Member: Dave Person <dperson@example.com> on aardvark@example.com as MemberRole.member> - >>> for member in mlist.members.members: + >>> for member in sorted(mlist.members.members, key=sort_key): ... print(member) <Member: aperson@example.com on aardvark@example.com as MemberRole.member> <Member: bperson@example.com on aardvark@example.com as MemberRole.member> @@ -133,7 +136,7 @@ just by changing their preferred address. >>> new_address.verified_on = now() >>> user.preferred_address = new_address - >>> for member in mlist.members.members: + >>> for member in sorted(mlist.members.members, key=sort_key): ... print(member) <Member: aperson@example.com on aardvark@example.com as MemberRole.member> <Member: bperson@example.com on aardvark@example.com as MemberRole.member> diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py index 083e1cf51..ef8b1f761 100644 --- a/src/mailman/model/domain.py +++ b/src/mailman/model/domain.py @@ -48,7 +48,7 @@ class Domain(Model): id = Column(Integer, primary_key=True) - mail_host = Column(Unicode) + mail_host = Column(Unicode) # TODO: add index? base_url = Column(Unicode) description = Column(Unicode) contact_address = Column(Unicode) @@ -170,7 +170,7 @@ class DomainManager: @dbconnection def __iter__(self, store): """See `IDomainManager`.""" - for domain in store.query(Domain).all(): + for domain in store.query(Domain).order_by(Domain.mail_host).all(): yield domain @dbconnection diff --git a/src/mailman/model/listmanager.py b/src/mailman/model/listmanager.py index 43a2b8f2a..261490a92 100644 --- a/src/mailman/model/listmanager.py +++ b/src/mailman/model/listmanager.py @@ -86,7 +86,8 @@ class ListManager: @dbconnection def mailing_lists(self, store): """See `IListManager`.""" - for mlist in store.query(MailingList).all(): + for mlist in store.query(MailingList).order_by( + MailingList._list_id).all(): yield mlist @dbconnection diff --git a/src/mailman/rest/docs/moderation.rst b/src/mailman/rest/docs/moderation.rst index 44182eb23..6e2dbb43c 100644 --- a/src/mailman/rest/docs/moderation.rst +++ b/src/mailman/rest/docs/moderation.rst @@ -226,10 +226,9 @@ moderator approval. >>> from mailman.app.moderator import hold_subscription >>> from mailman.interfaces.member import DeliveryMode - >>> hold_subscription( + >>> sub_req_id = hold_subscription( ... ant, 'anne@example.com', 'Anne Person', ... 'password', DeliveryMode.regular, 'en') - 1 >>> transaction.commit() The subscription request is available from the mailing list. @@ -242,7 +241,7 @@ The subscription request is available from the mailing list. http_etag: "..." language: en password: password - request_id: 1 + request_id: ... type: subscription when: 2005-08-01T07:49:23 http_etag: "..." @@ -259,8 +258,7 @@ Bart tries to leave a mailing list, but he may not be allowed to. >>> from mailman.app.moderator import hold_unsubscription >>> bart = add_member(ant, 'bart@example.com', 'Bart Person', ... 'password', DeliveryMode.regular, 'en') - >>> hold_unsubscription(ant, 'bart@example.com') - 2 + >>> unsub_req_id = hold_unsubscription(ant, 'bart@example.com') >>> transaction.commit() The unsubscription request is also available from the mailing list. @@ -273,13 +271,13 @@ The unsubscription request is also available from the mailing list. http_etag: "..." language: en password: password - request_id: 1 + request_id: ... type: subscription when: 2005-08-01T07:49:23 entry 1: address: bart@example.com http_etag: "..." - request_id: 2 + request_id: ... type: unsubscription http_etag: "..." start: 0 @@ -292,23 +290,25 @@ Viewing individual requests You can view an individual membership change request by providing the request id. Anne's subscription request looks like this. - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests/1') + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' + ... 'requests/{}'.format(sub_req_id)) address: anne@example.com delivery_mode: regular display_name: Anne Person http_etag: "..." language: en password: password - request_id: 1 + request_id: ... type: subscription when: 2005-08-01T07:49:23 Bart's unsubscription request looks like this. - >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/requests/2') + >>> dump_json('http://localhost:9001/3.0/lists/ant@example.com/' + ... 'requests/{}'.format(unsub_req_id)) address: bart@example.com http_etag: "..." - request_id: 2 + request_id: ... type: unsubscription @@ -328,9 +328,8 @@ data requires an action of one of the following: Anne's subscription request is accepted. >>> dump_json('http://localhost:9001/3.0/lists/' - ... 'ant@example.com/requests/1', { - ... 'action': 'accept', - ... }) + ... 'ant@example.com/requests/{}'.format(sub_req_id), + ... {'action': 'accept'}) content-length: 0 date: ... server: ... @@ -347,9 +346,8 @@ Anne is now a member of the mailing list. Bart's unsubscription request is discarded. >>> dump_json('http://localhost:9001/3.0/lists/' - ... 'ant@example.com/requests/2', { - ... 'action': 'discard', - ... }) + ... 'ant@example.com/requests/{}'.format(unsub_req_id), + ... {'action': 'discard'}) content-length: 0 date: ... server: ... diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py index 4a9841fc2..6104e64f7 100644 --- a/src/mailman/testing/layers.py +++ b/src/mailman/testing/layers.py @@ -190,10 +190,11 @@ class ConfigLayer(MockAndMonkeyLayer): @classmethod def tearDown(cls): assert cls.var_dir is not None, 'Layer not set up' - # Reset the test database after the tests are done so that there is no - # data in case the tests are rerun with a database layer like mysql or - # postgresql which are not deleted in teardown. reset_the_world() + # Destroy the test database after the tests are done so that there is + # no data in case the tests are rerun with a database layer like mysql + # or postgresql which are not deleted in teardown. + config.db.destroy() config.pop('test config') shutil.rmtree(cls.var_dir) cls.var_dir = None diff --git a/src/mailman/testing/testing.cfg b/src/mailman/testing/testing.cfg index 43ae6e67e..ff2034069 100644 --- a/src/mailman/testing/testing.cfg +++ b/src/mailman/testing/testing.cfg @@ -18,9 +18,9 @@ # A testing configuration. # For testing against PostgreSQL. -# [database] -# class: mailman.database.postgresql.PostgreSQLDatabase -# url: postgresql://$USER:$USER@localhost/mailman_test +[database] +class: mailman.database.postgresql.PostgreSQLDatabase +url: postgresql://barry:barry@localhost:5433/mailman [mailman] site_owner: noreply@example.com |
