Merge lp:~abompard/mailman/moderation_reasons into lp:mailman
- moderation_reasons
- Merge into 3.0
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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Barry Warsaw | Approve | ||
Review via email: mp+216392@code.launchpad.net |
Commit message
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.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/mailman/app/docs/chains.rst' | |||
2 | --- src/mailman/app/docs/chains.rst 2012-03-07 23:45:09 +0000 | |||
3 | +++ src/mailman/app/docs/chains.rst 2014-04-17 20:19:21 +0000 | |||
4 | @@ -132,7 +132,7 @@ | |||
5 | 132 | List: test@example.com | 132 | List: test@example.com |
6 | 133 | From: aperson@example.com | 133 | From: aperson@example.com |
7 | 134 | Subject: My first post | 134 | Subject: My first post |
9 | 135 | Reason: XXX | 135 | Reasons: (n/a) |
10 | 136 | <BLANKLINE> | 136 | <BLANKLINE> |
11 | 137 | At your convenience, visit: | 137 | At your convenience, visit: |
12 | 138 | <BLANKLINE> | 138 | <BLANKLINE> |
13 | @@ -189,7 +189,7 @@ | |||
14 | 189 | <BLANKLINE> | 189 | <BLANKLINE> |
15 | 190 | The reason it is being held: | 190 | The reason it is being held: |
16 | 191 | <BLANKLINE> | 191 | <BLANKLINE> |
18 | 192 | XXX | 192 | (n/a) |
19 | 193 | <BLANKLINE> | 193 | <BLANKLINE> |
20 | 194 | Either the message will get posted to the list, or you will receive | 194 | Either the message will get posted to the list, or you will receive |
21 | 195 | notification of the moderator's decision. If you would like to cancel | 195 | notification of the moderator's decision. If you would like to cancel |
22 | 196 | 196 | ||
23 | === modified file 'src/mailman/chains/hold.py' | |||
24 | --- src/mailman/chains/hold.py 2014-01-01 14:59:42 +0000 | |||
25 | +++ src/mailman/chains/hold.py 2014-04-17 20:19:21 +0000 | |||
26 | @@ -26,6 +26,7 @@ | |||
27 | 26 | 26 | ||
28 | 27 | 27 | ||
29 | 28 | import logging | 28 | import logging |
30 | 29 | import re | ||
31 | 29 | 30 | ||
32 | 30 | from email.mime.message import MIMEMessage | 31 | from email.mime.message import MIMEMessage |
33 | 31 | from email.mime.text import MIMEText | 32 | from email.mime.text import MIMEText |
34 | @@ -138,7 +139,6 @@ | |||
35 | 138 | if rule_misses: | 139 | if rule_misses: |
36 | 139 | msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) | 140 | msg['X-Mailman-Rule-Misses'] = SEMISPACE.join(rule_misses) |
37 | 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. |
38 | 141 | # XXX How to calculate the reason? | ||
39 | 142 | request_id = hold_message(mlist, msg, msgdata, None) | 142 | request_id = hold_message(mlist, msg, msgdata, None) |
40 | 143 | # Calculate a confirmation token to send to the author of the | 143 | # Calculate a confirmation token to send to the author of the |
41 | 144 | # message. | 144 | # message. |
42 | @@ -158,11 +158,13 @@ | |||
43 | 158 | original_subject = _('(no subject)') | 158 | original_subject = _('(no subject)') |
44 | 159 | else: | 159 | else: |
45 | 160 | original_subject = oneline(original_subject, charset) | 160 | original_subject = oneline(original_subject, charset) |
46 | 161 | reasons = msgdata.get("moderation_reasons", [_("(n/a)")]) | ||
47 | 161 | substitutions = dict( | 162 | substitutions = dict( |
48 | 162 | listname = mlist.fqdn_listname, | 163 | listname = mlist.fqdn_listname, |
49 | 163 | subject = original_subject, | 164 | subject = original_subject, |
50 | 164 | sender = msg.sender, | 165 | sender = msg.sender, |
52 | 165 | reason = 'XXX', #reason, | 166 | reasons = "\r\n".join( |
53 | 167 | [' ' + wrap(_(r), column=66) for r in reasons]), | ||
54 | 166 | confirmurl = '{0}/{1}'.format(mlist.script_url('confirm'), token), | 168 | confirmurl = '{0}/{1}'.format(mlist.script_url('confirm'), token), |
55 | 167 | admindb_url = mlist.script_url('admindb'), | 169 | admindb_url = mlist.script_url('admindb'), |
56 | 168 | ) | 170 | ) |
57 | @@ -209,7 +211,8 @@ | |||
58 | 209 | charset = language.charset | 211 | charset = language.charset |
59 | 210 | # We need to regenerate or re-translate a few values in the | 212 | # We need to regenerate or re-translate a few values in the |
60 | 211 | # substitution dictionary. | 213 | # substitution dictionary. |
62 | 212 | #d['reason'] = _(reason) # XXX reason | 214 | substitutions["reasons"] = wrap( |
63 | 215 | ", ".join([_(r) for r in reasons]), column=55) | ||
64 | 213 | substitutions['subject'] = original_subject | 216 | substitutions['subject'] = original_subject |
65 | 214 | # craft the admin notification message and deliver it | 217 | # craft the admin notification message and deliver it |
66 | 215 | subject = _( | 218 | subject = _( |
67 | @@ -240,9 +243,7 @@ | |||
68 | 240 | nmsg.attach(MIMEMessage(dmsg)) | 243 | nmsg.attach(MIMEMessage(dmsg)) |
69 | 241 | nmsg.send(mlist, **dict(tomoderators=True)) | 244 | nmsg.send(mlist, **dict(tomoderators=True)) |
70 | 242 | # Log the held message | 245 | # Log the held message |
71 | 243 | # XXX reason | ||
72 | 244 | reason = 'n/a' | ||
73 | 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', |
74 | 246 | mlist.fqdn_listname, msg.sender, | 247 | mlist.fqdn_listname, msg.sender, |
76 | 247 | msg.get('message-id', 'n/a'), reason) | 248 | msg.get('message-id', 'n/a'), " ; ".join(reasons)) |
77 | 248 | notify(HoldEvent(mlist, msg, msgdata, self)) | 249 | notify(HoldEvent(mlist, msg, msgdata, self)) |
78 | 249 | 250 | ||
79 | === modified file 'src/mailman/chains/tests/test_hold.py' | |||
80 | --- src/mailman/chains/tests/test_hold.py 2014-01-01 14:59:42 +0000 | |||
81 | +++ src/mailman/chains/tests/test_hold.py 2014-04-17 20:19:21 +0000 | |||
82 | @@ -29,10 +29,14 @@ | |||
83 | 29 | from zope.component import getUtility | 29 | from zope.component import getUtility |
84 | 30 | 30 | ||
85 | 31 | from mailman.app.lifecycle import create_list | 31 | from mailman.app.lifecycle import create_list |
87 | 32 | from mailman.chains.hold import autorespond_to_sender | 32 | from mailman.chains.hold import autorespond_to_sender, HoldChain |
88 | 33 | from mailman.email.message import Message | ||
89 | 33 | from mailman.interfaces.autorespond import IAutoResponseSet, Response | 34 | from mailman.interfaces.autorespond import IAutoResponseSet, Response |
90 | 34 | from mailman.interfaces.usermanager import IUserManager | 35 | from mailman.interfaces.usermanager import IUserManager |
92 | 35 | from mailman.testing.helpers import configuration, get_queue_messages | 36 | from mailman.testing.helpers import ( |
93 | 37 | configuration, | ||
94 | 38 | get_queue_messages, | ||
95 | 39 | specialized_message_from_string as mfs) | ||
96 | 36 | from mailman.testing.layers import ConfigLayer | 40 | from mailman.testing.layers import ConfigLayer |
97 | 37 | 41 | ||
98 | 38 | 42 | ||
99 | @@ -89,3 +93,48 @@ | |||
100 | 89 | 93 | ||
101 | 90 | If you believe this message is in error, or if you have any questions, | 94 | If you believe this message is in error, or if you have any questions, |
102 | 91 | please contact the list owner at test-owner@example.com.""") | 95 | please contact the list owner at test-owner@example.com.""") |
103 | 96 | |||
104 | 97 | |||
105 | 98 | |||
106 | 92 | 99 | ||
107 | 100 | class TestHoldChain(unittest.TestCase): | ||
108 | 101 | """Test the hold chain code.""" | ||
109 | 102 | |||
110 | 103 | layer = ConfigLayer | ||
111 | 104 | |||
112 | 105 | def setUp(self): | ||
113 | 106 | self._mlist = create_list('test@example.com') | ||
114 | 107 | |||
115 | 108 | def test_hold_chain(self): | ||
116 | 109 | msg = mfs("""\ | ||
117 | 110 | From: anne@example.com | ||
118 | 111 | To: test@example.com | ||
119 | 112 | Subject: A message | ||
120 | 113 | Message-ID: <ant> | ||
121 | 114 | MIME-Version: 1.0 | ||
122 | 115 | |||
123 | 116 | A message body. | ||
124 | 117 | """) | ||
125 | 118 | metadata = { | ||
126 | 119 | "moderation_reasons": [ | ||
127 | 120 | "TEST-REASON-1", | ||
128 | 121 | "TEST-REASON-2", | ||
129 | 122 | ], | ||
130 | 123 | } | ||
131 | 124 | chain = HoldChain() | ||
132 | 125 | chain._process(self._mlist, msg, metadata) | ||
133 | 126 | messages = get_queue_messages('virgin') | ||
134 | 127 | self.assertEqual(len(messages), 2) | ||
135 | 128 | sent_msgs = {} | ||
136 | 129 | for msg_bag in messages: | ||
137 | 130 | if msg_bag.msg["To"] == "test-owner@example.com": | ||
138 | 131 | sent_msgs["owner"] = msg_bag.msg | ||
139 | 132 | elif msg_bag.msg["To"] == "anne@example.com": | ||
140 | 133 | sent_msgs["sender"] = msg_bag.msg | ||
141 | 134 | else: | ||
142 | 135 | self.fail("Unexpected message: %s" % msg_bag.msg) | ||
143 | 136 | self.assertTrue("TEST-REASON-1, TEST-REASON-2" in | ||
144 | 137 | sent_msgs["owner"].as_string()) | ||
145 | 138 | self.assertTrue(" TEST-REASON-1" in | ||
146 | 139 | sent_msgs["sender"].as_string()) | ||
147 | 140 | self.assertTrue(" TEST-REASON-2" in | ||
148 | 141 | sent_msgs["sender"].as_string()) | ||
149 | 93 | 142 | ||
150 | === modified file 'src/mailman/rules/docs/moderation.rst' | |||
151 | --- src/mailman/rules/docs/moderation.rst 2013-06-19 02:43:40 +0000 | |||
152 | +++ src/mailman/rules/docs/moderation.rst 2014-04-17 20:19:21 +0000 | |||
153 | @@ -51,8 +51,9 @@ | |||
154 | 51 | >>> member_rule.check(mlist, member_msg, msgdata) | 51 | >>> member_rule.check(mlist, member_msg, msgdata) |
155 | 52 | True | 52 | True |
156 | 53 | >>> dump_msgdata(msgdata) | 53 | >>> dump_msgdata(msgdata) |
159 | 54 | moderation_action: hold | 54 | moderation_action : hold |
160 | 55 | moderation_sender: aperson@example.com | 55 | moderation_reasons: [u'The message comes from a moderated member'] |
161 | 56 | moderation_sender : aperson@example.com | ||
162 | 56 | 57 | ||
163 | 57 | 58 | ||
164 | 58 | Nonmembers | 59 | Nonmembers |
165 | @@ -87,8 +88,9 @@ | |||
166 | 87 | >>> nonmember_rule.check(mlist, nonmember_msg, msgdata) | 88 | >>> nonmember_rule.check(mlist, nonmember_msg, msgdata) |
167 | 88 | True | 89 | True |
168 | 89 | >>> dump_msgdata(msgdata) | 90 | >>> dump_msgdata(msgdata) |
171 | 90 | moderation_action: hold | 91 | moderation_action : hold |
172 | 91 | moderation_sender: bperson@example.com | 92 | moderation_reasons: [u'The message is not from a list member'] |
173 | 93 | moderation_sender : bperson@example.com | ||
174 | 92 | 94 | ||
175 | 93 | Of course, the nonmember action can be set to defer the decision, in which | 95 | Of course, the nonmember action can be set to defer the decision, in which |
176 | 94 | case the rule does not match. | 96 | case the rule does not match. |
177 | @@ -140,8 +142,9 @@ | |||
178 | 140 | >>> nonmember_rule.check(mlist, msg, msgdata) | 142 | >>> nonmember_rule.check(mlist, msg, msgdata) |
179 | 141 | True | 143 | True |
180 | 142 | >>> dump_msgdata(msgdata) | 144 | >>> dump_msgdata(msgdata) |
183 | 143 | moderation_action: hold | 145 | moderation_action : hold |
184 | 144 | moderation_sender: cperson@example.com | 146 | moderation_reasons: [u'The message is not from a list member'] |
185 | 147 | moderation_sender : cperson@example.com | ||
186 | 145 | 148 | ||
187 | 146 | >>> dump_list(mlist.members.members, key=memberkey) | 149 | >>> dump_list(mlist.members.members, key=memberkey) |
188 | 147 | <Member: Anne Person <aperson@example.com> | 150 | <Member: Anne Person <aperson@example.com> |
189 | 148 | 151 | ||
190 | === modified file 'src/mailman/rules/moderation.py' | |||
191 | --- src/mailman/rules/moderation.py 2014-03-15 19:13:46 +0000 | |||
192 | +++ src/mailman/rules/moderation.py 2014-04-17 20:19:21 +0000 | |||
193 | @@ -59,6 +59,10 @@ | |||
194 | 59 | # stored in the pending request table. | 59 | # stored in the pending request table. |
195 | 60 | msgdata['moderation_action'] = action.name | 60 | msgdata['moderation_action'] = action.name |
196 | 61 | msgdata['moderation_sender'] = sender | 61 | msgdata['moderation_sender'] = sender |
197 | 62 | if 'moderation_reasons' not in msgdata: | ||
198 | 63 | msgdata['moderation_reasons'] = [] | ||
199 | 64 | msgdata['moderation_reasons'].append( | ||
200 | 65 | _('The message comes from a moderated member')) | ||
201 | 62 | return True | 66 | return True |
202 | 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. |
203 | 64 | return False | 68 | return False |
204 | @@ -104,6 +108,10 @@ | |||
205 | 104 | # stored in the pending request table. | 108 | # stored in the pending request table. |
206 | 105 | msgdata['moderation_action'] = action.name | 109 | msgdata['moderation_action'] = action.name |
207 | 106 | msgdata['moderation_sender'] = sender | 110 | msgdata['moderation_sender'] = sender |
208 | 111 | if 'moderation_reasons' not in msgdata: | ||
209 | 112 | msgdata['moderation_reasons'] = [] | ||
210 | 113 | msgdata['moderation_reasons'].append( | ||
211 | 114 | _('The message is not from a list member')) | ||
212 | 107 | return True | 115 | return True |
213 | 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. |
214 | 109 | return False | 117 | return False |
215 | 110 | 118 | ||
216 | === modified file 'src/mailman/rules/tests/test_moderation.py' | |||
217 | --- src/mailman/rules/tests/test_moderation.py 2014-03-15 19:13:46 +0000 | |||
218 | +++ src/mailman/rules/tests/test_moderation.py 2014-04-17 20:19:21 +0000 | |||
219 | @@ -28,6 +28,7 @@ | |||
220 | 28 | import unittest | 28 | import unittest |
221 | 29 | 29 | ||
222 | 30 | from mailman.app.lifecycle import create_list | 30 | from mailman.app.lifecycle import create_list |
223 | 31 | from mailman.interfaces.action import Action | ||
224 | 31 | from mailman.interfaces.member import MemberRole | 32 | from mailman.interfaces.member import MemberRole |
225 | 32 | from mailman.interfaces.usermanager import IUserManager | 33 | from mailman.interfaces.usermanager import IUserManager |
226 | 33 | from mailman.rules import moderation | 34 | from mailman.rules import moderation |
227 | @@ -76,3 +77,37 @@ | |||
228 | 76 | # Bill is not a member. | 77 | # Bill is not a member. |
229 | 77 | bill_member = self._mlist.members.get_member('bill@example.com') | 78 | bill_member = self._mlist.members.get_member('bill@example.com') |
230 | 78 | self.assertIsNone(bill_member) | 79 | self.assertIsNone(bill_member) |
231 | 80 | |||
232 | 81 | def test_moderation_reason(self): | ||
233 | 82 | user_manager = getUtility(IUserManager) | ||
234 | 83 | anne = user_manager.create_address('anne@example.com') | ||
235 | 84 | msg = mfs("""\ | ||
236 | 85 | From: anne@example.com | ||
237 | 86 | To: test@example.com | ||
238 | 87 | Subject: A test message | ||
239 | 88 | Message-ID: <ant> | ||
240 | 89 | MIME-Version: 1.0 | ||
241 | 90 | |||
242 | 91 | A message body. | ||
243 | 92 | """) | ||
244 | 93 | # Anne is in the message's senders list. | ||
245 | 94 | self.assertIn('anne@example.com', msg.senders) | ||
246 | 95 | # Now run the rule | ||
247 | 96 | rule = moderation.NonmemberModeration() | ||
248 | 97 | msgdata = {} | ||
249 | 98 | result = rule.check(self._mlist, msg, msgdata) | ||
250 | 99 | self.assertTrue(result, 'NonmemberModeration rule should hit') | ||
251 | 100 | # The reason for moderation should be in the msgdata | ||
252 | 101 | self.assertTrue("moderation_reasons" in msgdata) | ||
253 | 102 | self.assertEqual(len(msgdata["moderation_reasons"]), 1) | ||
254 | 103 | # Now make Anne a moderated member | ||
255 | 104 | anne_member = self._mlist.subscribe(anne, MemberRole.member) | ||
256 | 105 | anne_member.moderation_action = Action.hold | ||
257 | 106 | # ...and run the rule again | ||
258 | 107 | rule = moderation.MemberModeration() | ||
259 | 108 | msgdata = {} | ||
260 | 109 | result = rule.check(self._mlist, msg, msgdata) | ||
261 | 110 | self.assertTrue(result, 'MemberModeration rule should hit') | ||
262 | 111 | # The reason for moderation should be in the msgdata | ||
263 | 112 | self.assertTrue("moderation_reasons" in msgdata) | ||
264 | 113 | self.assertEqual(len(msgdata["moderation_reasons"]), 1) | ||
265 | 79 | 114 | ||
266 | === modified file 'src/mailman/templates/en/postauth.txt' | |||
267 | --- src/mailman/templates/en/postauth.txt 2008-09-29 06:44:19 +0000 | |||
268 | +++ src/mailman/templates/en/postauth.txt 2014-04-17 20:19:21 +0000 | |||
269 | @@ -4,10 +4,10 @@ | |||
270 | 4 | List: $listname | 4 | List: $listname |
271 | 5 | From: $sender | 5 | From: $sender |
272 | 6 | Subject: $subject | 6 | Subject: $subject |
274 | 7 | Reason: $reason | 7 | Reasons: $reasons |
275 | 8 | 8 | ||
276 | 9 | At your convenience, visit: | 9 | At your convenience, visit: |
277 | 10 | 10 | ||
278 | 11 | $admindb_url | 11 | $admindb_url |
280 | 12 | 12 | ||
281 | 13 | to approve or deny the request. | 13 | to approve or deny the request. |
282 | 14 | 14 | ||
283 | === modified file 'src/mailman/templates/en/postheld.txt' | |||
284 | --- src/mailman/templates/en/postheld.txt 2008-09-29 06:44:19 +0000 | |||
285 | +++ src/mailman/templates/en/postheld.txt 2014-04-17 20:19:21 +0000 | |||
286 | @@ -6,7 +6,7 @@ | |||
287 | 6 | 6 | ||
288 | 7 | The reason it is being held: | 7 | The reason it is being held: |
289 | 8 | 8 | ||
291 | 9 | $reason | 9 | $reasons |
292 | 10 | 10 | ||
293 | 11 | Either the message will get posted to the list, or you will receive | 11 | Either the message will get posted to the list, or you will receive |
294 | 12 | notification of the moderator's decision. If you would like to cancel | 12 | notification of the moderator's decision. If you would like to cancel |
Thanks for the contribution. I've adapted this to the current git trunk and will apply over there.