Merge lp:~abompard/mailman/moderation_reasons into lp:mailman

Proposed by Aurélien Bompard
Status: Merged
Merge reported by: Barry Warsaw
Merged at revision: not available
Proposed branch: lp:~abompard/mailman/moderation_reasons
Merge into: lp:mailman
Diff against target: 293 lines (+115/-19)
8 files modified
src/mailman/app/docs/chains.rst (+2/-2)
src/mailman/chains/hold.py (+7/-6)
src/mailman/chains/tests/test_hold.py (+51/-2)
src/mailman/rules/docs/moderation.rst (+9/-6)
src/mailman/rules/moderation.py (+8/-0)
src/mailman/rules/tests/test_moderation.py (+35/-0)
src/mailman/templates/en/postauth.txt (+2/-2)
src/mailman/templates/en/postheld.txt (+1/-1)
To merge this branch: bzr merge lp:~abompard/mailman/moderation_reasons
Reviewer Review Type Date Requested Status
Barry Warsaw Approve
Review via email: mp+216392@code.launchpad.net

Description of the change

Currently moderation reasons are always 'XXX'. This branch fixes that with a message that comes from the moderation rules that matched.

To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :

Thanks for the contribution. I've adapted this to the current git trunk and will apply over there.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/mailman/app/docs/chains.rst'
--- src/mailman/app/docs/chains.rst 2012-03-07 23:45:09 +0000
+++ src/mailman/app/docs/chains.rst 2014-04-17 20:19:21 +0000
@@ -132,7 +132,7 @@
132 List: test@example.com132 List: test@example.com
133 From: aperson@example.com133 From: aperson@example.com
134 Subject: My first post134 Subject: My first post
135 Reason: XXX135 Reasons: (n/a)
136 <BLANKLINE>136 <BLANKLINE>
137 At your convenience, visit:137 At your convenience, visit:
138 <BLANKLINE>138 <BLANKLINE>
@@ -189,7 +189,7 @@
189 <BLANKLINE>189 <BLANKLINE>
190 The reason it is being held:190 The reason it is being held:
191 <BLANKLINE>191 <BLANKLINE>
192 XXX192 (n/a)
193 <BLANKLINE>193 <BLANKLINE>
194 Either the message will get posted to the list, or you will receive194 Either the message will get posted to the list, or you will receive
195 notification of the moderator's decision. If you would like to cancel195 notification of the moderator's decision. If you would like to cancel
196196
=== modified file 'src/mailman/chains/hold.py'
--- src/mailman/chains/hold.py 2014-01-01 14:59:42 +0000
+++ src/mailman/chains/hold.py 2014-04-17 20:19:21 +0000
@@ -26,6 +26,7 @@
2626
2727
28import logging28import logging
29import re
2930
30from email.mime.message import MIMEMessage31from email.mime.message import MIMEMessage
31from email.mime.text import MIMEText32from email.mime.text import MIMEText
@@ -138,7 +139,6 @@
138 if rule_misses:139 if rule_misses:
139 msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses)140 msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses)
140 # Hold the message by adding it to the list's request database.141 # Hold the message by adding it to the list's request database.
141 # XXX How to calculate the reason?
142 request_id = hold_message(mlist, msg, msgdata, None)142 request_id = hold_message(mlist, msg, msgdata, None)
143 # Calculate a confirmation token to send to the author of the143 # Calculate a confirmation token to send to the author of the
144 # message.144 # message.
@@ -158,11 +158,13 @@
158 original_subject = _('(no subject)')158 original_subject = _('(no subject)')
159 else:159 else:
160 original_subject = oneline(original_subject, charset)160 original_subject = oneline(original_subject, charset)
161 reasons = msgdata.get("moderation_reasons", [_("(n/a)")])
161 substitutions = dict(162 substitutions = dict(
162 listname = mlist.fqdn_listname,163 listname = mlist.fqdn_listname,
163 subject = original_subject,164 subject = original_subject,
164 sender = msg.sender,165 sender = msg.sender,
165 reason = 'XXX', #reason,166 reasons = "\r\n".join(
167 [' ' + wrap(_(r), column=66) for r in reasons]),
166 confirmurl = '{0}/{1}'.format(mlist.script_url('confirm'), token),168 confirmurl = '{0}/{1}'.format(mlist.script_url('confirm'), token),
167 admindb_url = mlist.script_url('admindb'),169 admindb_url = mlist.script_url('admindb'),
168 )170 )
@@ -209,7 +211,8 @@
209 charset = language.charset211 charset = language.charset
210 # We need to regenerate or re-translate a few values in the212 # We need to regenerate or re-translate a few values in the
211 # substitution dictionary.213 # substitution dictionary.
212 #d['reason'] = _(reason) # XXX reason214 substitutions["reasons"] = wrap(
215 ", ".join([_(r) for r in reasons]), column=55)
213 substitutions['subject'] = original_subject216 substitutions['subject'] = original_subject
214 # craft the admin notification message and deliver it217 # craft the admin notification message and deliver it
215 subject = _(218 subject = _(
@@ -240,9 +243,7 @@
240 nmsg.attach(MIMEMessage(dmsg))243 nmsg.attach(MIMEMessage(dmsg))
241 nmsg.send(mlist, **dict(tomoderators=True))244 nmsg.send(mlist, **dict(tomoderators=True))
242 # Log the held message245 # Log the held message
243 # XXX reason
244 reason = 'n/a'
245 log.info('HOLD: %s post from %s held, message-id=%s: %s',246 log.info('HOLD: %s post from %s held, message-id=%s: %s',
246 mlist.fqdn_listname, msg.sender,247 mlist.fqdn_listname, msg.sender,
247 msg.get('message-id', 'n/a'), reason)248 msg.get('message-id', 'n/a'), " ; ".join(reasons))
248 notify(HoldEvent(mlist, msg, msgdata, self))249 notify(HoldEvent(mlist, msg, msgdata, self))
249250
=== modified file 'src/mailman/chains/tests/test_hold.py'
--- src/mailman/chains/tests/test_hold.py 2014-01-01 14:59:42 +0000
+++ src/mailman/chains/tests/test_hold.py 2014-04-17 20:19:21 +0000
@@ -29,10 +29,14 @@
29from zope.component import getUtility29from zope.component import getUtility
3030
31from mailman.app.lifecycle import create_list31from mailman.app.lifecycle import create_list
32from mailman.chains.hold import autorespond_to_sender32from mailman.chains.hold import autorespond_to_sender, HoldChain
33from mailman.email.message import Message
33from mailman.interfaces.autorespond import IAutoResponseSet, Response34from mailman.interfaces.autorespond import IAutoResponseSet, Response
34from mailman.interfaces.usermanager import IUserManager35from mailman.interfaces.usermanager import IUserManager
35from mailman.testing.helpers import configuration, get_queue_messages36from mailman.testing.helpers import (
37 configuration,
38 get_queue_messages,
39 specialized_message_from_string as mfs)
36from mailman.testing.layers import ConfigLayer40from mailman.testing.layers import ConfigLayer
3741
3842
@@ -89,3 +93,48 @@
8993
90If you believe this message is in error, or if you have any questions,94If you believe this message is in error, or if you have any questions,
91please contact the list owner at test-owner@example.com.""")95please contact the list owner at test-owner@example.com.""")
96
97
98
9299
100class TestHoldChain(unittest.TestCase):
101 """Test the hold chain code."""
102
103 layer = ConfigLayer
104
105 def setUp(self):
106 self._mlist = create_list('test@example.com')
107
108 def test_hold_chain(self):
109 msg = mfs("""\
110From: anne@example.com
111To: test@example.com
112Subject: A message
113Message-ID: <ant>
114MIME-Version: 1.0
115
116A message body.
117""")
118 metadata = {
119 "moderation_reasons": [
120 "TEST-REASON-1",
121 "TEST-REASON-2",
122 ],
123 }
124 chain = HoldChain()
125 chain._process(self._mlist, msg, metadata)
126 messages = get_queue_messages('virgin')
127 self.assertEqual(len(messages), 2)
128 sent_msgs = {}
129 for msg_bag in messages:
130 if msg_bag.msg["To"] == "test-owner@example.com":
131 sent_msgs["owner"] = msg_bag.msg
132 elif msg_bag.msg["To"] == "anne@example.com":
133 sent_msgs["sender"] = msg_bag.msg
134 else:
135 self.fail("Unexpected message: %s" % msg_bag.msg)
136 self.assertTrue("TEST-REASON-1, TEST-REASON-2" in
137 sent_msgs["owner"].as_string())
138 self.assertTrue(" TEST-REASON-1" in
139 sent_msgs["sender"].as_string())
140 self.assertTrue(" TEST-REASON-2" in
141 sent_msgs["sender"].as_string())
93142
=== modified file 'src/mailman/rules/docs/moderation.rst'
--- src/mailman/rules/docs/moderation.rst 2013-06-19 02:43:40 +0000
+++ src/mailman/rules/docs/moderation.rst 2014-04-17 20:19:21 +0000
@@ -51,8 +51,9 @@
51 >>> member_rule.check(mlist, member_msg, msgdata)51 >>> member_rule.check(mlist, member_msg, msgdata)
52 True52 True
53 >>> dump_msgdata(msgdata)53 >>> dump_msgdata(msgdata)
54 moderation_action: hold54 moderation_action : hold
55 moderation_sender: aperson@example.com55 moderation_reasons: [u'The message comes from a moderated member']
56 moderation_sender : aperson@example.com
5657
5758
58Nonmembers59Nonmembers
@@ -87,8 +88,9 @@
87 >>> nonmember_rule.check(mlist, nonmember_msg, msgdata)88 >>> nonmember_rule.check(mlist, nonmember_msg, msgdata)
88 True89 True
89 >>> dump_msgdata(msgdata)90 >>> dump_msgdata(msgdata)
90 moderation_action: hold91 moderation_action : hold
91 moderation_sender: bperson@example.com92 moderation_reasons: [u'The message is not from a list member']
93 moderation_sender : bperson@example.com
9294
93Of course, the nonmember action can be set to defer the decision, in which95Of course, the nonmember action can be set to defer the decision, in which
94case the rule does not match.96case the rule does not match.
@@ -140,8 +142,9 @@
140 >>> nonmember_rule.check(mlist, msg, msgdata)142 >>> nonmember_rule.check(mlist, msg, msgdata)
141 True143 True
142 >>> dump_msgdata(msgdata)144 >>> dump_msgdata(msgdata)
143 moderation_action: hold145 moderation_action : hold
144 moderation_sender: cperson@example.com146 moderation_reasons: [u'The message is not from a list member']
147 moderation_sender : cperson@example.com
145148
146 >>> dump_list(mlist.members.members, key=memberkey)149 >>> dump_list(mlist.members.members, key=memberkey)
147 <Member: Anne Person <aperson@example.com>150 <Member: Anne Person <aperson@example.com>
148151
=== modified file 'src/mailman/rules/moderation.py'
--- src/mailman/rules/moderation.py 2014-03-15 19:13:46 +0000
+++ src/mailman/rules/moderation.py 2014-04-17 20:19:21 +0000
@@ -59,6 +59,10 @@
59 # stored in the pending request table.59 # stored in the pending request table.
60 msgdata['moderation_action'] = action.name60 msgdata['moderation_action'] = action.name
61 msgdata['moderation_sender'] = sender61 msgdata['moderation_sender'] = sender
62 if 'moderation_reasons' not in msgdata:
63 msgdata['moderation_reasons'] = []
64 msgdata['moderation_reasons'].append(
65 _('The message comes from a moderated member'))
62 return True66 return True
63 # The sender is not a member so this rule does not match.67 # The sender is not a member so this rule does not match.
64 return False68 return False
@@ -104,6 +108,10 @@
104 # stored in the pending request table.108 # stored in the pending request table.
105 msgdata['moderation_action'] = action.name109 msgdata['moderation_action'] = action.name
106 msgdata['moderation_sender'] = sender110 msgdata['moderation_sender'] = sender
111 if 'moderation_reasons' not in msgdata:
112 msgdata['moderation_reasons'] = []
113 msgdata['moderation_reasons'].append(
114 _('The message is not from a list member'))
107 return True115 return True
108 # The sender must be a member, so this rule does not match.116 # The sender must be a member, so this rule does not match.
109 return False117 return False
110118
=== modified file 'src/mailman/rules/tests/test_moderation.py'
--- src/mailman/rules/tests/test_moderation.py 2014-03-15 19:13:46 +0000
+++ src/mailman/rules/tests/test_moderation.py 2014-04-17 20:19:21 +0000
@@ -28,6 +28,7 @@
28import unittest28import unittest
2929
30from mailman.app.lifecycle import create_list30from mailman.app.lifecycle import create_list
31from mailman.interfaces.action import Action
31from mailman.interfaces.member import MemberRole32from mailman.interfaces.member import MemberRole
32from mailman.interfaces.usermanager import IUserManager33from mailman.interfaces.usermanager import IUserManager
33from mailman.rules import moderation34from mailman.rules import moderation
@@ -76,3 +77,37 @@
76 # Bill is not a member.77 # Bill is not a member.
77 bill_member = self._mlist.members.get_member('bill@example.com')78 bill_member = self._mlist.members.get_member('bill@example.com')
78 self.assertIsNone(bill_member)79 self.assertIsNone(bill_member)
80
81 def test_moderation_reason(self):
82 user_manager = getUtility(IUserManager)
83 anne = user_manager.create_address('anne@example.com')
84 msg = mfs("""\
85From: anne@example.com
86To: test@example.com
87Subject: A test message
88Message-ID: <ant>
89MIME-Version: 1.0
90
91A message body.
92""")
93 # Anne is in the message's senders list.
94 self.assertIn('anne@example.com', msg.senders)
95 # Now run the rule
96 rule = moderation.NonmemberModeration()
97 msgdata = {}
98 result = rule.check(self._mlist, msg, msgdata)
99 self.assertTrue(result, 'NonmemberModeration rule should hit')
100 # The reason for moderation should be in the msgdata
101 self.assertTrue("moderation_reasons" in msgdata)
102 self.assertEqual(len(msgdata["moderation_reasons"]), 1)
103 # Now make Anne a moderated member
104 anne_member = self._mlist.subscribe(anne, MemberRole.member)
105 anne_member.moderation_action = Action.hold
106 # ...and run the rule again
107 rule = moderation.MemberModeration()
108 msgdata = {}
109 result = rule.check(self._mlist, msg, msgdata)
110 self.assertTrue(result, 'MemberModeration rule should hit')
111 # The reason for moderation should be in the msgdata
112 self.assertTrue("moderation_reasons" in msgdata)
113 self.assertEqual(len(msgdata["moderation_reasons"]), 1)
79114
=== modified file 'src/mailman/templates/en/postauth.txt'
--- src/mailman/templates/en/postauth.txt 2008-09-29 06:44:19 +0000
+++ src/mailman/templates/en/postauth.txt 2014-04-17 20:19:21 +0000
@@ -4,10 +4,10 @@
4 List: $listname4 List: $listname
5 From: $sender5 From: $sender
6 Subject: $subject6 Subject: $subject
7 Reason: $reason7 Reasons: $reasons
88
9At your convenience, visit:9At your convenience, visit:
1010
11 $admindb_url11 $admindb_url
12 12
13to approve or deny the request.13to approve or deny the request.
1414
=== modified file 'src/mailman/templates/en/postheld.txt'
--- src/mailman/templates/en/postheld.txt 2008-09-29 06:44:19 +0000
+++ src/mailman/templates/en/postheld.txt 2014-04-17 20:19:21 +0000
@@ -6,7 +6,7 @@
66
7The reason it is being held:7The reason it is being held:
88
9 $reason9$reasons
1010
11Either the message will get posted to the list, or you will receive11Either the message will get posted to the list, or you will receive
12notification of the moderator's decision. If you would like to cancel12notification of the moderator's decision. If you would like to cancel