summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Warsaw2007-12-30 02:47:45 -0500
committerBarry Warsaw2007-12-30 02:47:45 -0500
commit5b4bb22feca4d520afef44d1c472807e020d17b5 (patch)
tree8d85431b8d129fad6c29dabd4d03c2eabe8a11d7
parent66ffab7c0d56b8144a80045fac3a7dab036a597f (diff)
downloadmailman-5b4bb22feca4d520afef44d1c472807e020d17b5.tar.gz
mailman-5b4bb22feca4d520afef44d1c472807e020d17b5.tar.zst
mailman-5b4bb22feca4d520afef44d1c472807e020d17b5.zip
-rw-r--r--Mailman/Message.py5
-rw-r--r--Mailman/database/mailman.sql1
-rw-r--r--Mailman/database/member.py2
-rw-r--r--Mailman/docs/moderation.txt71
-rw-r--r--Mailman/docs/no-subject.txt35
-rw-r--r--Mailman/interfaces/member.py3
-rw-r--r--Mailman/rules/moderation.py69
-rw-r--r--Mailman/rules/no_subject.py45
8 files changed, 230 insertions, 1 deletions
diff --git a/Mailman/Message.py b/Mailman/Message.py
index 89bc42798..a9256dc90 100644
--- a/Mailman/Message.py
+++ b/Mailman/Message.py
@@ -58,7 +58,10 @@ class Message(email.message.Message):
return value
def get_all(self, name, failobj=None):
- all_values = email.message.Message.get_all(self, name, failobj)
+ missing = object()
+ all_values = email.message.Message.get_all(self, name, missing)
+ if all_values is missing:
+ return failobj
return [(unicode(value, 'ascii') if isinstance(value, str) else value)
for value in all_values]
diff --git a/Mailman/database/mailman.sql b/Mailman/database/mailman.sql
index cff4daba0..c511e6180 100644
--- a/Mailman/database/mailman.sql
+++ b/Mailman/database/mailman.sql
@@ -145,6 +145,7 @@ CREATE TABLE member (
id INTEGER NOT NULL,
role TEXT,
mailing_list TEXT,
+ is_moderated BOOLEAN,
address_id INTEGER,
preferences_id INTEGER,
PRIMARY KEY (id),
diff --git a/Mailman/database/member.py b/Mailman/database/member.py
index f77b8c7c3..b24688423 100644
--- a/Mailman/database/member.py
+++ b/Mailman/database/member.py
@@ -33,6 +33,7 @@ class Member(Model):
id = Int(primary=True)
role = Enum()
mailing_list = Unicode()
+ is_moderated = Bool()
address_id = Int()
address = Reference(address_id, 'Address.id')
@@ -43,6 +44,7 @@ class Member(Model):
self.role = role
self.mailing_list = mailing_list
self.address = address
+ self.is_moderated = False
def __repr__(self):
return '<Member: %s on %s as %s>' % (
diff --git a/Mailman/docs/moderation.txt b/Mailman/docs/moderation.txt
new file mode 100644
index 000000000..0ce6bee6e
--- /dev/null
+++ b/Mailman/docs/moderation.txt
@@ -0,0 +1,71 @@
+Member moderation
+=================
+
+Each user has a moderation flag. When set, and the list is set to moderate
+postings, then only members with a cleared moderation flag will be able to
+email the list without having those messages be held for approval. The
+'moderation' rule determines whether the message should be moderated or not.
+
+ >>> from Mailman.configuration import config
+ >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
+ >>> from Mailman.app.rules import find_rule
+ >>> rule = find_rule('moderation')
+ >>> rule.name
+ 'moderation'
+
+In the simplest case, the sender is not a member of the mailing list, so the
+moderation rule can't match.
+
+ >>> msg = message_from_string(u"""\
+ ... From: aperson@example.org
+ ... To: _xtest@example.com
+ ... Subject: A posted message
+ ...
+ ... """)
+ >>> rule.check(mlist, msg, {})
+ False
+
+Let's add the message author as a non-moderated member.
+
+ >>> user = config.db.user_manager.create_user(
+ ... u'aperson@example.org', u'Anne Person')
+ >>> address = list(user.addresses)[0]
+ >>> from Mailman.interfaces import MemberRole
+ >>> member = address.subscribe(mlist, MemberRole.member)
+ >>> member.is_moderated
+ False
+ >>> rule.check(mlist, msg, {})
+ False
+
+Once the member's moderation flag is set though, the rule matches.
+
+ >>> member.is_moderated = True
+ >>> rule.check(mlist, msg, {})
+ True
+
+
+Non-members
+-----------
+
+There is another, related rule for matching non-members, which simply matches
+if the sender is /not/ a member of the mailing list.
+
+ >>> rule = find_rule('non-member')
+ >>> rule.name
+ 'non-member'
+
+If the sender is a member of this mailing list, the rule does not match.
+
+ >>> rule.check(mlist, msg, {})
+ False
+
+But if the sender is not a member of this mailing list, the rule matches.
+
+ >>> msg = message_from_string(u"""\
+ ... From: bperson@example.org
+ ... To: _xtest@example.com
+ ... Subject: A posted message
+ ...
+ ... """)
+ >>> rule.check(mlist, msg, {})
+ True
diff --git a/Mailman/docs/no-subject.txt b/Mailman/docs/no-subject.txt
new file mode 100644
index 000000000..3c6dc88bf
--- /dev/null
+++ b/Mailman/docs/no-subject.txt
@@ -0,0 +1,35 @@
+No Subject header
+=================
+
+This rule matches if the message has no Subject header, or if the header is
+the empty string when stripped.
+
+ >>> from Mailman.configuration import config
+ >>> mlist = config.db.list_manager.create(u'_xtest@example.com')
+ >>> from Mailman.app.rules import find_rule
+ >>> rule = find_rule('no-subject')
+ >>> rule.name
+ 'no-subject'
+
+A message with a non-empty subject does not match the rule.
+
+ >>> msg = message_from_string(u"""\
+ ... From: aperson@example.org
+ ... To: _xtest@example.com
+ ... Subject: A posted message
+ ...
+ ... """)
+ >>> rule.check(mlist, msg, {})
+ False
+
+Delete the Subject header and the rule matches.
+
+ >>> del msg['subject']
+ >>> rule.check(mlist, msg, {})
+ True
+
+Even a Subject header with only whitespace still matches the rule.
+
+ >>> msg['Subject'] = u' '
+ >>> rule.check(mlist, msg, {})
+ True
diff --git a/Mailman/interfaces/member.py b/Mailman/interfaces/member.py
index 18f0b034e..8fc410a77 100644
--- a/Mailman/interfaces/member.py
+++ b/Mailman/interfaces/member.py
@@ -78,6 +78,9 @@ class IMember(Interface):
role = Attribute(
"""The role of this membership.""")
+ is_moderated = Attribute(
+ """True if the membership is moderated, otherwise False.""")
+
def unsubscribe():
"""Unsubscribe (and delete) this member from the mailing list."""
diff --git a/Mailman/rules/moderation.py b/Mailman/rules/moderation.py
new file mode 100644
index 000000000..5b0820426
--- /dev/null
+++ b/Mailman/rules/moderation.py
@@ -0,0 +1,69 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""Membership related rules."""
+
+__all__ = [
+ 'moderation_rule',
+ 'nonmember_rule',
+ ]
+__metaclass__ = type
+
+
+from zope.interface import implements
+
+from Mailman.i18n import _
+from Mailman.interfaces import IRule
+
+
+
+class Moderation:
+ """The member moderation rule."""
+ implements(IRule)
+
+ name = 'moderation'
+ description = _('Match messages sent by moderated members.')
+
+ def check(self, mlist, msg, msgdata):
+ """See `IRule`."""
+ for sender in msg.get_senders():
+ member = mlist.members.get_member(sender)
+ if member is not None and member.is_moderated:
+ return True
+ return False
+
+
+
+class NonMember:
+ """The non-membership rule."""
+ implements(IRule)
+
+ name = 'non-member'
+ description = _('Match messages sent by non-members.')
+
+ def check(self, mlist, msg, msgdata):
+ """See `IRule`."""
+ for sender in msg.get_senders():
+ if mlist.members.get_member(sender) is not None:
+ # The sender is a member of the mailing list.
+ return False
+ return True
+
+
+
+moderation_rule = Moderation()
+nonmember_rule = NonMember()
diff --git a/Mailman/rules/no_subject.py b/Mailman/rules/no_subject.py
new file mode 100644
index 000000000..ca7cbd9d2
--- /dev/null
+++ b/Mailman/rules/no_subject.py
@@ -0,0 +1,45 @@
+# Copyright (C) 2007 by the Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+"""The no-Subject header rule."""
+
+__all__ = ['no_subject_rule']
+__metaclass__ = type
+
+
+from zope.interface import implements
+
+from Mailman.i18n import _
+from Mailman.interfaces import IRule
+
+
+
+class NoSubject:
+ """The no-Subject rule."""
+ implements(IRule)
+
+ name = 'no-subject'
+ description = _('Catch messages with no, or empty, Subject headers.')
+
+ def check(self, mlist, msg, msgdata):
+ """See `IRule`."""
+ subject = msg.get('subject', '').strip()
+ return subject == ''
+
+
+
+no_subject_rule = NoSubject()