diff options
| -rw-r--r-- | Mailman/Defaults.py | 14 | ||||
| -rw-r--r-- | Mailman/app/styles.py | 22 | ||||
| -rw-r--r-- | Mailman/database/listmanager.py | 6 | ||||
| -rw-r--r-- | Mailman/database/model/mailinglist.py | 2 | ||||
| -rw-r--r-- | Mailman/database/model/requests.py | 16 | ||||
| -rw-r--r-- | Mailman/docs/digests.txt | 4 | ||||
| -rw-r--r-- | Mailman/docs/news-runner.txt | 10 | ||||
| -rw-r--r-- | Mailman/docs/replybot.txt | 47 | ||||
| -rw-r--r-- | Mailman/docs/requests.txt | 52 | ||||
| -rw-r--r-- | Mailman/docs/runner.txt | 12 | ||||
| -rw-r--r-- | Mailman/docs/scrubber.txt | 30 | ||||
| -rw-r--r-- | Mailman/docs/styles.txt | 8 | ||||
| -rw-r--r-- | Mailman/docs/subject-munging.txt | 77 | ||||
| -rw-r--r-- | Mailman/docs/tagger.txt | 43 | ||||
| -rw-r--r-- | Mailman/queue/__init__.py | 3 |
15 files changed, 139 insertions, 207 deletions
diff --git a/Mailman/Defaults.py b/Mailman/Defaults.py index a5234bdd9..7175894bf 100644 --- a/Mailman/Defaults.py +++ b/Mailman/Defaults.py @@ -455,7 +455,7 @@ NNTP_USERNAME = None NNTP_PASSWORD = None # Set this if you have an NNTP server you prefer gatewayed lists to use. -DEFAULT_NNTP_HOST = '' +DEFAULT_NNTP_HOST = u'' # These variables controls how headers must be cleansed in order to be # accepted by your NNTP server. Some servers like INN reject messages @@ -809,7 +809,7 @@ MAX_RESTARTS = 10 # The default language for this server. Whenever we can't figure out the list # context or user context, we'll fall back to using this language. This code # must be in the list of available language codes. -DEFAULT_SERVER_LANGUAGE = 'en' +DEFAULT_SERVER_LANGUAGE = u'en' # When allowing only members to post to a mailing list, how is the sender of # the message determined? If this variable is set to Yes, then first the @@ -909,10 +909,10 @@ DEFAULT_MAX_MESSAGE_SIZE = 40 # KB # These format strings will be expanded w.r.t. the dictionary for the # mailing list instance. -DEFAULT_SUBJECT_PREFIX = "[%(real_name)s] " +DEFAULT_SUBJECT_PREFIX = u'[%(real_name)s] ' # DEFAULT_SUBJECT_PREFIX = "[%(real_name)s %%d]" # for numbering -DEFAULT_MSG_HEADER = "" -DEFAULT_MSG_FOOTER = """\ +DEFAULT_MSG_HEADER = u'' +DEFAULT_MSG_FOOTER = u"""\ _______________________________________________ $real_name mailing list $fqdn_realname @@ -978,7 +978,7 @@ DEFAULT_SEND_GOODBYE_MSG = Yes DEFAULT_ANONYMOUS_LIST = No # {header-name: regexp} spam filtering - we include some for example sake. -DEFAULT_BOUNCE_MATCHING_HEADERS = """ +DEFAULT_BOUNCE_MATCHING_HEADERS = u""" # Lines that *start* with a '#' are comments. to: friend@public.com message-id: relay.comanche.denmark.eu @@ -1098,7 +1098,7 @@ DEFAULT_NONDIGESTABLE = Yes # Will list be available in digested form? DEFAULT_DIGESTABLE = Yes -DEFAULT_DIGEST_HEADER = "" +DEFAULT_DIGEST_HEADER = u'' DEFAULT_DIGEST_FOOTER = DEFAULT_MSG_FOOTER DEFAULT_DIGEST_IS_DEFAULT = No diff --git a/Mailman/app/styles.py b/Mailman/app/styles.py index 39e8355db..4ca3f1b01 100644 --- a/Mailman/app/styles.py +++ b/Mailman/app/styles.py @@ -57,7 +57,7 @@ class DefaultStyle: mlist.max_num_recipients = config.DEFAULT_MAX_NUM_RECIPIENTS mlist.max_message_size = config.DEFAULT_MAX_MESSAGE_SIZE mlist.reply_goes_to_list = config.DEFAULT_REPLY_GOES_TO_LIST - mlist.reply_to_address = '' + mlist.reply_to_address = u'' mlist.first_strip_reply_to = config.DEFAULT_FIRST_STRIP_REPLY_TO mlist.admin_immed_notify = config.DEFAULT_ADMIN_IMMED_NOTIFY mlist.admin_notify_mchanges = ( @@ -72,10 +72,10 @@ class DefaultStyle: config.DEFAULT_BOUNCE_MATCHING_HEADERS) mlist.header_filter_rules = [] mlist.anonymous_list = config.DEFAULT_ANONYMOUS_LIST - mlist.description = '' - mlist.info = '' - mlist.welcome_msg = '' - mlist.goodbye_msg = '' + mlist.description = u'' + mlist.info = u'' + mlist.welcome_msg = u'' + mlist.goodbye_msg = u'' mlist.subscribe_policy = config.DEFAULT_SUBSCRIBE_POLICY mlist.subscribe_auto_approval = config.DEFAULT_SUBSCRIBE_AUTO_APPROVAL mlist.unsubscribe_policy = config.DEFAULT_UNSUBSCRIBE_POLICY @@ -120,7 +120,7 @@ class DefaultStyle: config.DEFAULT_ARCHIVE_VOLUME_FREQUENCY) mlist.emergency = False mlist.member_moderation_action = Action.hold - mlist.member_moderation_notice = '' + mlist.member_moderation_notice = u'' mlist.accept_these_nonmembers = [] mlist.hold_these_nonmembers = [] mlist.reject_these_nonmembers = [] @@ -128,7 +128,7 @@ class DefaultStyle: mlist.forward_auto_discards = config.DEFAULT_FORWARD_AUTO_DISCARDS mlist.generic_nonmember_action = ( config.DEFAULT_GENERIC_NONMEMBER_ACTION) - mlist.nonmember_rejection_notice = '' + mlist.nonmember_rejection_notice = u'' # Ban lists mlist.ban_list = [] # Max autoresponses per day. A mapping between addresses and a @@ -157,9 +157,9 @@ class DefaultStyle: # 1 - autorespond, but discard the original message # 2 - autorespond, and forward the message on to be processed mlist.autorespond_requests = 0 - mlist.autoresponse_postings_text = '' - mlist.autoresponse_admin_text = '' - mlist.autoresponse_request_text = '' + mlist.autoresponse_postings_text = u'' + mlist.autoresponse_admin_text = u'' + mlist.autoresponse_request_text = u'' mlist.autoresponse_graceperiod = datetime.timedelta(days=90) mlist.postings_responses = {} mlist.admin_responses = {} @@ -187,7 +187,7 @@ class DefaultStyle: mlist.delivery_status = {} # NNTP gateway mlist.nntp_host = config.DEFAULT_NNTP_HOST - mlist.linked_newsgroup = '' + mlist.linked_newsgroup = u'' mlist.gateway_to_news = False mlist.gateway_to_mail = False mlist.news_prefix_subject_too = True diff --git a/Mailman/database/listmanager.py b/Mailman/database/listmanager.py index 1a17cb96e..cd1f03fe2 100644 --- a/Mailman/database/listmanager.py +++ b/Mailman/database/listmanager.py @@ -49,8 +49,12 @@ class ListManager(object): mlist.delete() def get(self, fqdn_listname): + # Avoid circular imports. + from Mailman.database.model import MailingList listname, hostname = split_listname(fqdn_listname) - mlist = MailingList.get_by(list_name=listname, host_name=hostname) + mlist = config.db.store.find(MailingList, + list_name=listname, + host_name=hostname).one() if mlist is not None: # XXX Fixme mlist._restore() diff --git a/Mailman/database/model/mailinglist.py b/Mailman/database/model/mailinglist.py index ed34a3aae..3a3396758 100644 --- a/Mailman/database/model/mailinglist.py +++ b/Mailman/database/model/mailinglist.py @@ -120,7 +120,7 @@ class MailingList(Model): max_days_to_hold = Int() max_message_size = Int() max_num_recipients = Int() - member_moderation_action = Bool() + member_moderation_action = Enum() member_moderation_notice = Unicode() mime_is_default_digest = Bool() moderator_password = Unicode() diff --git a/Mailman/database/model/requests.py b/Mailman/database/model/requests.py index d16e8f49d..5e92e70b2 100644 --- a/Mailman/database/model/requests.py +++ b/Mailman/database/model/requests.py @@ -47,21 +47,25 @@ class ListRequests: @property def count(self): - return _Request.query.filter_by(mailing_list=self.mailing_list).count() + return config.db.store.find( + _Request, mailing_list=self.mailing_list).count() def count_of(self, request_type): - return _Request.query.filter_by(mailing_list=self.mailing_list, - type=request_type).count() + return config.db.store.find( + _Request, + mailing_list=self.mailing_list, type=request_type).count() @property def held_requests(self): - results = _Request.query.filter_by(mailing_list=self.mailing_list) + results = config.db.store.find( + _Request, mailing_list=self.mailing_list) for request in results: yield request def of_type(self, request_type): - results = _Request.query.filter_by(mailing_list=self.mailing_list, - type=request_type) + results = config.db.store.find( + _Request, + mailing_list=self.mailing_list, type=request_type) for request in results: yield request diff --git a/Mailman/docs/digests.txt b/Mailman/docs/digests.txt index 72c34b372..486d64098 100644 --- a/Mailman/docs/digests.txt +++ b/Mailman/docs/digests.txt @@ -417,7 +417,7 @@ XXX We also have to set the default server language to French, otherwise the English template will be found and the masthead won't be translated. >>> config.languages.enable_language('fr') - >>> config.DEFAULT_SERVER_LANGUAGE = 'fr' + >>> config.DEFAULT_SERVER_LANGUAGE = u'fr' >>> mlist.preferred_language = 'fr' >>> msg = message_from_string("""\ ... From: aperson@example.org @@ -539,4 +539,4 @@ Set the digest threshold to zero so that the digests will be sent immediately. Clean up -------- - >>> config.DEFAULT_SERVER_LANGUAGE = 'en' + >>> config.DEFAULT_SERVER_LANGUAGE = u'en' diff --git a/Mailman/docs/news-runner.txt b/Mailman/docs/news-runner.txt index 7adbc7add..bc6619f50 100644 --- a/Mailman/docs/news-runner.txt +++ b/Mailman/docs/news-runner.txt @@ -8,8 +8,8 @@ was originally written to gate to Usenet, which has its own rules). >>> from Mailman.configuration import config >>> from Mailman.queue.news import prepare_message - >>> mlist = config.db.list_manager.create('_xtest@example.com') - >>> mlist.linked_newsgroup = 'comp.lang.python' + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') + >>> mlist.linked_newsgroup = u'comp.lang.python' Some NNTP servers such as INN reject messages containing a set of prohibited headers, so one of the things that the news runner does is remove these @@ -128,7 +128,7 @@ address is added for the benefit of the Usenet system. ... """) >>> prepare_message(mlist, msg, {}) >>> msg['approved'] - '_xtest@example.com' + u'_xtest@example.com' >>> mlist.news_moderation = NewsModeration.moderated >>> msg = message_from_string("""\ @@ -139,7 +139,7 @@ address is added for the benefit of the Usenet system. ... """) >>> prepare_message(mlist, msg, {}) >>> msg['approved'] - '_xtest@example.com' + u'_xtest@example.com' But if the newsgroup is not moderated, the Approved: header is not chnaged. @@ -152,7 +152,7 @@ But if the newsgroup is not moderated, the Approved: header is not chnaged. ... """) >>> prepare_message(mlist, msg, {}) >>> msg['approved'] - "this doesn't get deleted" + u"this doesn't get deleted" XXX More of the NewsRunner should be tested. diff --git a/Mailman/docs/replybot.txt b/Mailman/docs/replybot.txt index 8ca131bf8..424fa2944 100644 --- a/Mailman/docs/replybot.txt +++ b/Mailman/docs/replybot.txt @@ -6,15 +6,11 @@ it receives on its posting address, or special robot addresses. Automatic responses are subject to various conditions, such as headers in the original message or the amount of time since the last auto-response. - >>> from email import message_from_string >>> from Mailman.Handlers.Replybot import process - >>> from Mailman.Message import Message >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> mlist = config.db.list_manager.create('_xtest@example.com') - >>> mlist.real_name = 'XTest' - >>> mlist.web_page_url = 'http://www.example.com/' - >>> flush() + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') + >>> mlist.real_name = u'XTest' + >>> mlist.web_page_url = u'http://www.example.com/' >>> # Ensure that the virgin queue is empty, since we'll be checking this >>> # for new auto-response messages. @@ -36,23 +32,22 @@ will be sent, with 0 meaning "there is no grace period". >>> import datetime >>> mlist.autorespond_admin = True >>> mlist.autoresponse_graceperiod = datetime.timedelta() - >>> mlist.autoresponse_admin_text = 'admin autoresponse text' - >>> flush() - >>> msg = message_from_string("""\ + >>> mlist.autoresponse_admin_text = u'admin autoresponse text' + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... To: _xtest-owner@example.com ... ... help - ... """, Message) + ... """) >>> process(mlist, msg, dict(toowner=True)) >>> len(virginq.files) 1 >>> qmsg, qdata = virginq.dequeue(virginq.files[0]) >>> # Print only some of the meta data. The rest is uninteresting. >>> qdata['listname'] - '_xtest@example.com' + u'_xtest@example.com' >>> sorted(qdata['recips']) - ['aperson@example.com'] + [u'aperson@example.com'] >>> # Delete data that is time dependent or random >>> del qmsg['message-id'] >>> del qmsg['date'] @@ -79,12 +74,12 @@ Several headers in the original message determine whether an autoresponse should even be sent. For example, if the message has an "X-Ack: No" header, no auto-response is sent. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... X-Ack: No ... ... help me - ... """, Message) + ... """) >>> process(mlist, msg, dict(toowner=True)) >>> virginq.files [] @@ -92,11 +87,11 @@ no auto-response is sent. Mailman itself can suppress autoresponses for certain types of internally crafted messages, by setting the 'noack' metadata key. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: mailman@example.com ... ... help for you - ... """, Message) + ... """) >>> process(mlist, msg, dict(noack=True, toowner=True)) >>> virginq.files [] @@ -104,12 +99,12 @@ crafted messages, by setting the 'noack' metadata key. If there is a Precedence: header with any of the values 'bulk', 'junk', or 'list', then the autoresponse is also suppressed. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: asystem@example.com ... Precedence: bulk ... ... hey! - ... """, Message) + ... """) >>> process(mlist, msg, dict(toowner=True)) >>> virginq.files [] @@ -155,14 +150,13 @@ with the text set for owner responses. Two other types of email will get auto-responses: those sent to the -request address... >>> mlist.autorespond_requests = True - >>> mlist.autoresponse_request_text = 'robot autoresponse text' - >>> flush() - >>> msg = message_from_string("""\ + >>> mlist.autoresponse_request_text = u'robot autoresponse text' + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... To: _xtest-request@example.com ... ... help me - ... """, Message) + ... """) >>> process(mlist, msg, dict(torequest=True)) >>> len(virginq.files) 1 @@ -185,14 +179,13 @@ auto-responses: those sent to the -request address... ...and those sent to the posting address. >>> mlist.autorespond_postings = True - >>> mlist.autoresponse_postings_text = 'postings autoresponse text' - >>> flush() - >>> msg = message_from_string("""\ + >>> mlist.autoresponse_postings_text = u'postings autoresponse text' + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... To: _xtest@example.com ... ... help me - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> len(virginq.files) 1 diff --git a/Mailman/docs/requests.txt b/Mailman/docs/requests.txt index 914ce7dfb..febf3e430 100644 --- a/Mailman/docs/requests.txt +++ b/Mailman/docs/requests.txt @@ -6,7 +6,6 @@ closed lists, or postings by non-members. The requests database is the low level interface to these actions requiring approval. >>> from Mailman.configuration import config - >>> from Mailman.database import flush Here is a helper function for printing out held requests. @@ -42,8 +41,7 @@ mailing list you need to get its requests object. >>> from zope.interface.verify import verifyObject >>> verifyObject(IRequests, config.db.requests) True - >>> mlist = config.db.list_manager.create('test@example.com') - >>> flush() + >>> mlist = config.db.list_manager.create(u'test@example.com') >>> requests = config.db.requests.get_list_requests(mlist) >>> verifyObject(IListRequests, requests) True @@ -73,7 +71,6 @@ data, except for the request type. Here we hold some simple bits of data. >>> id_4 = requests.hold_request(RequestType.held_message, 'hold_4') >>> id_1, id_2, id_3, id_4 (1, 2, 3, 4) - >>> flush() And of course, now we can see that there are four requests being held. @@ -102,7 +99,6 @@ We can hold requests with additional data. >>> data = dict(foo='yes', bar='no') >>> id_5 = requests.hold_request(RequestType.held_message, 'hold_5', data) - >>> flush() >>> id_5 5 >>> requests.count @@ -172,7 +168,6 @@ Once a specific request has been handled, it will be deleted from the requests database. >>> requests.delete_request(2) - >>> flush() >>> requests.count 4 >>> show_holds(requests) @@ -194,7 +189,6 @@ For the next section, we first clean up all the current requests. >>> for request in requests.held_requests: ... requests.delete_request(request.id) - >>> flush() >>> requests.count 0 @@ -218,16 +212,14 @@ For this section, we need a mailing list and at least one message. >>> mlist = config.db.list_manager.create('alist@example.com') >>> mlist.preferred_language = 'en' >>> mlist.real_name = 'A Test List' - >>> flush() >>> from email import message_from_string - >>> from Mailman.Message import Message - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.org ... To: alist@example.com ... Subject: Something important ... ... Here's something important about our mailing list. - ... """, Message) + ... """) Holding a message means keeping a copy of it that a moderator must approve before the message is posted to the mailing list. To hold the message, you @@ -235,7 +227,6 @@ must supply the message, message metadata, and a text reason for the hold. In this case, we won't include any additional metadata. >>> id_1 = moderator.hold_message(mlist, msg, {}, 'Needs approval') - >>> flush() >>> requests.get_request(id_1) is not None True @@ -245,7 +236,6 @@ We can also hold a message with some additional metadata. ... approved=True, ... received_time=123.45) >>> id_2 = moderator.hold_message(mlist, msg, msgdata, 'Feeling ornery') - >>> flush() >>> requests.get_request(id_2) is not None True @@ -254,7 +244,6 @@ trivial is to simply defer a decision for now. >>> from Mailman.interfaces import Action >>> moderator.handle_message(mlist, id_1, Action.defer) - >>> flush() >>> requests.get_request(id_1) is not None True @@ -262,7 +251,6 @@ The moderator can also discard the message. This is often done with spam. Bye bye message! >>> moderator.handle_message(mlist, id_1, Action.discard) - >>> flush() >>> print requests.get_request(id_1) None >>> virginq.files @@ -271,7 +259,6 @@ Bye bye message! The message can be rejected, meaning it is bounced back to the sender. >>> moderator.handle_message(mlist, id_2, Action.reject, 'Off topic') - >>> flush() >>> print requests.get_request(id_2) None >>> qmsg, qdata = dequeue() @@ -314,9 +301,7 @@ the incoming queue for further processing, however the message metadata indicates that the message has been approved. >>> id_3 = moderator.hold_message(mlist, msg, msgdata, 'Needs approval') - >>> flush() >>> moderator.handle_message(mlist, id_3, Action.accept) - >>> flush() >>> inq = Switchboard(config.INQUEUE_DIR) >>> qmsg, qdata = dequeue(inq) >>> print qmsg.as_string() @@ -344,18 +329,16 @@ moderator interface to also preserve a copy, essentially telling it not to delete the message from the storage. First, without the switch, the message is deleted. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.org ... To: alist@example.com ... Subject: Something important ... Message-ID: <12345> ... ... Here's something important about our mailing list. - ... """, Message) + ... """) >>> id_4 = moderator.hold_message(mlist, msg, {}, 'Needs approval') - >>> flush() >>> moderator.handle_message(mlist, id_4, Action.discard) - >>> flush() >>> msgs = config.db.message_store.get_messages_by_message_id('<12345>') >>> list(msgs) [] @@ -364,9 +347,7 @@ But if we ask to preserve the message when we discard it, it will be held in the message store after disposition. >>> id_4 = moderator.hold_message(mlist, msg, {}, 'Needs approval') - >>> flush() >>> moderator.handle_message(mlist, id_4, Action.discard, preserve=True) - >>> flush() >>> msgs = config.db.message_store.get_messages_by_message_id('<12345>') >>> msgs = list(msgs) >>> len(msgs) @@ -387,10 +368,8 @@ address. This is helpful for getting the message into the inbox of one of the moderators. >>> id_4 = moderator.hold_message(mlist, msg, {}, 'Needs approval') - >>> flush() >>> moderator.handle_message(mlist, id_4, Action.discard, ... forward=['zperson@example.com']) - >>> flush() >>> qmsg, qdata = dequeue() >>> print qmsg.as_string() Subject: Forward of moderated message @@ -429,11 +408,9 @@ chosing and their preferred language. >>> from Mailman.interfaces import DeliveryMode >>> mlist.admin_immed_notify = False - >>> flush() >>> id_3 = moderator.hold_subscription(mlist, ... 'bperson@example.org', 'Ben Person', ... '{NONE}abcxyz', DeliveryMode.regular, 'en') - >>> flush() >>> requests.get_request(id_3) is not None True @@ -452,11 +429,9 @@ queue when the message is held. >>> # XXX This will almost certainly change once we've worked out the web >>> # space layout for mailing lists now. >>> mlist.web_page_url = 'http://www.example.com/' - >>> flush() >>> id_4 = moderator.hold_subscription(mlist, ... 'cperson@example.org', 'Claire Person', ... '{NONE}zyxcba', DeliveryMode.regular, 'en') - >>> flush() >>> requests.get_request(id_4) is not None True >>> qmsg, qdata = dequeue() @@ -496,14 +471,12 @@ Once held, the moderator can select one of several dispositions. The most trivial is to simply defer a decision for now. >>> moderator.handle_subscription(mlist, id_3, Action.defer) - >>> flush() >>> requests.get_request(id_3) is not None True The held subscription can also be discarded. >>> moderator.handle_subscription(mlist, id_3, Action.discard) - >>> flush() >>> print requests.get_request(id_3) None @@ -512,7 +485,6 @@ subscriber. >>> moderator.handle_subscription(mlist, id_4, Action.reject, ... 'This is a closed list') - >>> flush() >>> print requests.get_request(id_4) None >>> qmsg, qdata = dequeue() @@ -555,7 +527,6 @@ mailing list. >>> id_4 = moderator.hold_subscription(mlist, ... 'fperson@example.org', 'Frank Person', ... '{NONE}abcxyz', DeliveryMode.regular, 'en') - >>> flush() A message will be sent to the moderators telling them about the held subscription and the fact that they may need to approve it. @@ -595,7 +566,6 @@ Accept the subscription request. >>> mlist.admin_notify_mchanges = True >>> moderator.handle_subscription(mlist, id_4, Action.accept) - >>> flush() There are now two messages in the virgin queue. One is a welcome message being sent to the user and the other is a subscription notification that is @@ -709,28 +679,22 @@ In this case, only the unsubscribing address is required. Like subscriptions, unsubscription holds can send the list's moderators an immediate notification. >>> mlist.admin_immed_notify = False - >>> flush() >>> from Mailman.interfaces import MemberRole >>> user_1 = config.db.user_manager.create_user('gperson@example.com') - >>> flush() >>> address_1 = list(user_1.addresses)[0] >>> address_1.subscribe(mlist, MemberRole.member) <Member: gperson@example.com on alist@example.com as MemberRole.member> >>> user_2 = config.db.user_manager.create_user('hperson@example.com') - >>> flush() >>> address_2 = list(user_2.addresses)[0] >>> address_2.subscribe(mlist, MemberRole.member) <Member: hperson@example.com on alist@example.com as MemberRole.member> - >>> flush() >>> id_5 = moderator.hold_unsubscription(mlist, 'gperson@example.com') - >>> flush() >>> requests.get_request(id_5) is not None True >>> virginq.files [] >>> mlist.admin_immed_notify = True >>> id_6 = moderator.hold_unsubscription(mlist, 'hperson@example.com') - >>> flush() >>> qmsg, qdata = dequeue() >>> print qmsg.as_string() MIME-Version: 1.0 @@ -766,7 +730,6 @@ There are now two addresses with held unsubscription requests. As above, one of the actions we can take is to defer to the decision. >>> moderator.handle_unsubscription(mlist, id_5, Action.defer) - >>> flush() >>> requests.get_request(id_5) is not None True @@ -774,7 +737,6 @@ The held unsubscription can also be discarded, and the member will remain subscribed. >>> moderator.handle_unsubscription(mlist, id_5, Action.discard) - >>> flush() >>> print requests.get_request(id_5) None >>> mlist.members.get_member('gperson@example.com') @@ -785,7 +747,6 @@ and the person remains a member of the mailing list. >>> moderator.handle_unsubscription(mlist, id_6, Action.reject, ... 'This list is a prison.') - >>> flush() >>> print requests.get_request(id_6) None >>> qmsg, qdata = dequeue() @@ -829,11 +790,8 @@ the mailing list. >>> mlist.send_goodbye_msg = True >>> mlist.goodbye_msg = 'So long!' >>> mlist.admin_immed_notify = False - >>> flush() >>> id_7 = moderator.hold_unsubscription(mlist, 'gperson@example.com') - >>> flush() >>> moderator.handle_unsubscription(mlist, id_7, Action.accept) - >>> flush() >>> print mlist.members.get_member('gperson@example.com') None diff --git a/Mailman/docs/runner.txt b/Mailman/docs/runner.txt index 75ec90758..4143c36a6 100644 --- a/Mailman/docs/runner.txt +++ b/Mailman/docs/runner.txt @@ -15,14 +15,10 @@ runners inherit from. This base class implements a .run() method that runs continuously in a loop until the .stop() method is called. >>> import os - >>> from email import message_from_string - >>> from Mailman.Message import Message >>> from Mailman.queue import Runner, Switchboard >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> mlist = config.db.list_manager.create('_xtest@example.com') - >>> mlist.preferred_language = 'en' - >>> flush() + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') + >>> mlist.preferred_language = u'en' Here is a very simple derived qrunner class. The class attribute QDIR tells the qrunner which queue directory it is responsible for. Derived classes @@ -50,12 +46,12 @@ This is about as simple as a qrunner can be. This qrunner doesn't do much except run once, storing the message and metadata on instance variables. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... To: _xtest@example.com ... ... A test message. - ... """, Message) + ... """) >>> filebase = switchboard.enqueue(msg, listname=mlist.fqdn_listname, ... foo='yes', bar='no') >>> runner.run() diff --git a/Mailman/docs/scrubber.txt b/Mailman/docs/scrubber.txt index 0c8c4d94f..e4259361d 100644 --- a/Mailman/docs/scrubber.txt +++ b/Mailman/docs/scrubber.txt @@ -7,13 +7,9 @@ scrub attachments from messages so that binary goop doesn't end up in an archive message. >>> from Mailman.Handlers.Scrubber import process, save_attachment - >>> from Mailman.Message import Message >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> from email import message_from_string - >>> mlist = config.db.list_manager.create('_xtest@example.com') - >>> mlist.preferred_language = 'en' - >>> flush() + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') + >>> mlist.preferred_language = u'en' Helper functions for getting the attachment data. @@ -56,15 +52,15 @@ enabled, the filename will be used when this header attribute is present (yes, this is an unfortunate double negative). >>> config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME = False - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Content-Type: image/gif; name="xtest.gif" ... Content-Transfer-Encoding: base64 ... Content-Disposition: attachment; filename="xtest.gif" ... ... R0lGODdhAQABAIAAAAAAAAAAACwAAAAAAQABAAACAQUAOw== - ... """, Message) + ... """) >>> save_attachment(mlist, msg, '') - '<http://www.example.com/pipermail/_xtest@example.com//xtest.gif>' + u'<http://www.example.com/pipermail/_xtest@example.com//xtest.gif>' >>> data = read_attachment('xtest.gif') >>> data[:6] 'GIF87a' @@ -85,19 +81,19 @@ Content-Disposition: filename. This is the default for reasons described in the Defaults.py.in file. >>> config.SCRUBBER_DONT_USE_ATTACHMENT_FILENAME = True - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Content-Type: image/gif; name="xtest.gif" ... Content-Transfer-Encoding: base64 ... Content-Disposition: attachment; filename="xtest.gif" ... ... R0lGODdhAQABAIAAAAAAAAAAACwAAAAAAQABAAACAQUAOw== - ... """, Message) + ... """) >>> save_attachment(mlist, msg, '') - '<http://www.example.com/pipermail/_xtest@example.com/.../attachment.gif>' + u'<http://www.example.com/pipermail/_xtest@example.com/.../attachment.gif>' >>> data = read_attachment('xtest.gif') Traceback (most recent call last): IOError: [Errno ...] No such file or directory: - '.../archives/private/_xtest@example.com/xtest.gif' + u'.../archives/private/_xtest@example.com/xtest.gif' >>> data = read_attachment('attachment.gif') >>> data[:6] 'GIF87a' @@ -111,7 +107,7 @@ Scrubbing image attachments When scrubbing image attachments, the original message is modified to include a reference to the attachment file as available through the on-line archive. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="BOUNDARY" ... @@ -126,7 +122,7 @@ a reference to the attachment file as available through the on-line archive. ... ... R0lGODdhAQABAIAAAAAAAAAAACwAAAAAAQABAAACAQUAOw== ... --BOUNDARY-- - ... """, Message) + ... """) >>> msgdata = {} The Scrubber.process() function is different than other handler process @@ -186,7 +182,7 @@ Scrubbing text attachments Similar to image attachments, text attachments will also be scrubbed, but the placeholder will be slightly different. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... MIME-Version: 1.0 ... Content-Type: multipart/mixed; boundary="BOUNDARY" ... @@ -200,7 +196,7 @@ placeholder will be slightly different. ... ... This is a text attachment. ... --BOUNDARY-- - ... """, Message) + ... """) >>> scrubbed_msg = process(mlist, msg, {}) >>> print scrubbed_msg.as_string() MIME-Version: 1.0 diff --git a/Mailman/docs/styles.txt b/Mailman/docs/styles.txt index 12e2744b6..b90302e21 100644 --- a/Mailman/docs/styles.txt +++ b/Mailman/docs/styles.txt @@ -14,8 +14,7 @@ modify the mailing list any way it wants. Let's start with a vanilla mailing list and a default style manager. >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> mlist = config.db.list_manager.create('_xtest@example.com') + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> from Mailman.app.styles import style_manager @@ -51,7 +50,6 @@ would pick this up and apply it to the mailing list. None >>> config.DEFAULT_MSG_FOOTER = u'default footer' >>> default_style.apply(mlist) - >>> flush() >>> mlist.msg_footer u'default footer' @@ -87,7 +85,6 @@ styles match the mailing list. ['test'] >>> for style in style_manager.lookup(mlist): ... style.apply(mlist) - >>> flush() >>> mlist.msg_footer u'test footer' @@ -107,13 +104,11 @@ applied last. ... mailing_list.msg_footer = u'another footer' >>> mlist.msg_footer = u'' - >>> flush() >>> mlist.msg_footer u'' >>> style_manager.register(AnotherTestStyle()) >>> for style in style_manager.lookup(mlist): ... style.apply(mlist) - >>> flush() >>> mlist.msg_footer u'another footer' @@ -126,7 +121,6 @@ will take effect in the new priority order. >>> style_2.priority = 10 >>> for style in style_manager.lookup(mlist): ... style.apply(mlist) - >>> flush() >>> mlist.msg_footer u'test footer' diff --git a/Mailman/docs/subject-munging.txt b/Mailman/docs/subject-munging.txt index 1ceebd415..388a02564 100644 --- a/Mailman/docs/subject-munging.txt +++ b/Mailman/docs/subject-munging.txt @@ -7,14 +7,10 @@ transformations. Some headers get added, others get changed. Some of these changes depend on mailing list settings and others depend on how the message is getting sent through the system. We'll take things one-by-one. - >>> from email import message_from_string - >>> from Mailman.Message import Message >>> from Mailman.Handlers.CookHeaders import process >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> mlist = config.db.list_manager.create('_xtest@example.com') + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') >>> mlist.subject_prefix = u'' - >>> flush() Inserting a prefix @@ -27,12 +23,11 @@ munging, a mailing list must have a preferred language. >>> mlist.subject_prefix = u'[XTest] ' >>> mlist.preferred_language = u'en' - >>> flush() - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... ... A message of great import. - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) @@ -48,12 +43,12 @@ email.header.Header instance which has an unhelpful repr. If the original message had a Subject header, then the prefix is inserted at the beginning of the header's value. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: Something important ... ... A message of great import. - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> msgdata['origsubj'] @@ -63,39 +58,39 @@ the beginning of the header's value. Subject headers are not munged for digest messages. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: Something important ... ... A message of great import. - ... """, Message) + ... """) >>> process(mlist, msg, dict(isdigest=True)) >>> msg['subject'] - 'Something important' + u'Something important' Nor are they munged for 'fast tracked' messages, which are generally defined as messages that Mailman crafts internally. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: Something important ... ... A message of great import. - ... """, Message) + ... """) >>> process(mlist, msg, dict(_fasttrack=True)) >>> msg['subject'] - 'Something important' + u'Something important' If a Subject header already has a prefix, usually following a Re: marker, another one will not be added but the prefix will be moved to the front of the header text. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: Re: [XTest] Something important ... ... A message of great import. - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest] Re: Something important @@ -104,12 +99,12 @@ If the Subjec header has a prefix at the front of the header text, that's where it will stay. This is called 'new style' prefixing and is the only option available in Mailman 3. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: [XTest] Re: Something important ... ... A message of great import. - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest] Re: Something important @@ -123,10 +118,10 @@ prefixes. Part of what makes this interesting is the encoding of i18n headers using RFC 2047, and lists whose preferred language is in a different character set than the encoded header. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest] =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= @@ -142,13 +137,12 @@ message is posted to the mailing list, a 'post id' gets incremented. This is a purely sequential integer that increases monotonically. By added a '%d' placeholder to the subject prefix, this post id can be included in the prefix. - >>> mlist.subject_prefix = '[XTest %d] ' + >>> mlist.subject_prefix = u'[XTest %d] ' >>> mlist.post_id = 456 - >>> flush() - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: Something important ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest 456] Something important @@ -157,10 +151,10 @@ This works even when the message is a reply, except that in this case, the numeric post id in the generated subject prefix is updated with the new post id. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: [XTest 123] Re: Something important ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest 456] Re: Something important @@ -168,10 +162,10 @@ id. If the Subject header had old style prefixing, the prefix is moved to the front of the header text. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: Re: [XTest 123] Something important ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest 456] Re: Something important @@ -180,10 +174,10 @@ front of the header text. And of course, the proper thing is done when posting id numbers are included in the subject prefix, and the subject is encoded non-ascii. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest 456] =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= @@ -193,10 +187,10 @@ in the subject prefix, and the subject is encoded non-ascii. Even more fun is when the i18n Subject header already has a prefix, possibly with a different posting number. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: [XTest 123] Re: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest 456] Re: =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= @@ -207,10 +201,10 @@ with a different posting number. As before, old style subject prefixes are re-ordered. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: Re: [XTest 123] =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest 456] Re: @@ -225,24 +219,23 @@ In this test case, we get an extra space between the prefix and the original subject. It's because the original is 'crooked'. Note that a Subject starting with '\n ' is generated by some version of Eudora Japanese edition. - >>> mlist.subject_prefix = '[XTest] ' - >>> flush() - >>> msg = message_from_string("""\ + >>> mlist.subject_prefix = u'[XTest] ' + >>> msg = message_from_string(u"""\ ... Subject: ... Important message ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) >>> print msg['subject'] [XTest] Important message And again, with an RFC 2047 encoded header. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: ... =?iso-2022-jp?b?GyRCJWEhPCVrJV4lcxsoQg==?= ... - ... """, Message) + ... """) >>> process(mlist, msg, {}) # XXX This one does not appear to work the same way as diff --git a/Mailman/docs/tagger.txt b/Mailman/docs/tagger.txt index a6b2d8ca8..3792f0f7f 100644 --- a/Mailman/docs/tagger.txt +++ b/Mailman/docs/tagger.txt @@ -9,12 +9,9 @@ its Subject: and Keywords: headers compared against these regular expressions. The message then gets tagged with the topic names of each hit. >>> from Mailman.Handlers.Tagger import process - >>> from Mailman.Message import Message >>> from Mailman.queue import Switchboard >>> from Mailman.configuration import config - >>> from Mailman.database import flush - >>> from email import message_from_string - >>> mlist = config.db.list_manager.create('_xtest@example.com') + >>> mlist = config.db.list_manager.create(u'_xtest@example.com') Topics must be enabled for Mailman to do any topic matching, even if topics are defined. @@ -22,13 +19,12 @@ are defined. >>> mlist.topics = [('bar fight', '.*bar.*', 'catch any bars', False)] >>> mlist.topics_enabled = False >>> mlist.topics_bodylines_limit = 0 - >>> flush() - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: foobar ... Keywords: barbaz ... - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg.as_string() @@ -44,12 +40,11 @@ artifacts of tagging; an X-Topics: header is added with the topic name, and the message metadata gets a key with a list of matching topic names. >>> mlist.topics_enabled = True - >>> flush() - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: foobar ... Keywords: barbaz ... - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg.as_string() @@ -69,7 +64,7 @@ The tagger can also look at a certain number of body lines, but only for Subject: and Keyword: header-like lines. When set to zero, no body lines are scanned. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: nothing ... Keywords: at all @@ -77,7 +72,7 @@ scanned. ... X-Ignore: something else ... Subject: foobar ... Keywords: barbaz - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg.as_string() @@ -96,8 +91,7 @@ But let the tagger scan a few body lines and the matching headers will be found. >>> mlist.topics_bodylines_limit = 5 - >>> flush() - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: nothing ... Keywords: at all @@ -105,7 +99,7 @@ found. ... X-Ignore: something else ... Subject: foobar ... Keywords: barbaz - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg.as_string() @@ -124,7 +118,7 @@ found. However, scanning stops at the first body line that doesn't look like a header. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: nothing ... Keywords: at all @@ -132,7 +126,7 @@ header. ... This is not a header ... Subject: foobar ... Keywords: barbaz - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg.as_string() @@ -149,9 +143,8 @@ header. When set to a negative number, all body lines will be scanned. >>> mlist.topics_bodylines_limit = -1 - >>> flush() >>> lots_of_headers = '\n'.join(['X-Ignore: zip'] * 100) - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... From: aperson@example.com ... Subject: nothing ... Keywords: at all @@ -159,13 +152,13 @@ When set to a negative number, all body lines will be scanned. ... %s ... Subject: foobar ... Keywords: barbaz - ... """ % lots_of_headers, Message) + ... """ % lots_of_headers) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> # Rather than print out 100 X-Ignore: headers, let's just prove that >>> # the X-Topics: header exists, meaning that the tagger did its job. >>> msg['x-topics'] - 'bar fight' + u'bar fight' >>> msgdata['topichits'] ['bar fight'] @@ -177,7 +170,7 @@ The tagger will also scan the body lines of text subparts in a multipart message, using the same rules as if all those body lines lived in a single text payload. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: Was ... Keywords: Raw ... Content-Type: multipart/alternative; boundary="BOUNDARY" @@ -190,7 +183,7 @@ text payload. ... Keywords: barbaz ... ... --BOUNDARY-- - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg.as_string() @@ -213,7 +206,7 @@ text payload. But the tagger will not descend into non-text parts. - >>> msg = message_from_string("""\ + >>> msg = message_from_string(u"""\ ... Subject: Was ... Keywords: Raw ... Content-Type: multipart/alternative; boundary=BOUNDARY @@ -235,7 +228,7 @@ But the tagger will not descend into non-text parts. ... Keywords: barbaz ... ... --BOUNDARY-- - ... """, Message) + ... """) >>> msgdata = {} >>> process(mlist, msg, msgdata) >>> print msg['x-topics'] diff --git a/Mailman/queue/__init__.py b/Mailman/queue/__init__.py index 94f747245..c415834ba 100644 --- a/Mailman/queue/__init__.py +++ b/Mailman/queue/__init__.py @@ -37,6 +37,7 @@ import logging import marshal import traceback +from cStringIO import StringIO from zope.interface import implements from Mailman import i18n @@ -108,7 +109,7 @@ class Switchboard: # Always add the metadata schema version number data['version'] = config.QFILE_SCHEMA_VERSION # Filter out volatile entries - for k in data: + for k in data.keys(): if k.startswith('_'): del data[k] # We have to tell the dequeue() method whether to parse the message |
