summaryrefslogtreecommitdiff
path: root/Mailman/Gui
diff options
context:
space:
mode:
authortkikuchi2005-08-28 05:31:27 +0000
committertkikuchi2005-08-28 05:31:27 +0000
commit067dc15b2432bb285ab5e4a3eac6f4dddd67ed19 (patch)
treeceac72251ee33742bfff7626c99dde163d3da946 /Mailman/Gui
parentbc1dad4f90a26ade7c4dd6d2863de88856e8b4b6 (diff)
downloadmailman-067dc15b2432bb285ab5e4a3eac6f4dddd67ed19.tar.gz
mailman-067dc15b2432bb285ab5e4a3eac6f4dddd67ed19.tar.zst
mailman-067dc15b2432bb285ab5e4a3eac6f4dddd67ed19.zip
back porting from 2.1.6
Diffstat (limited to 'Mailman/Gui')
-rw-r--r--Mailman/Gui/Bounce.py22
-rw-r--r--Mailman/Gui/ContentFilter.py48
-rw-r--r--Mailman/Gui/GUIBase.py15
-rw-r--r--Mailman/Gui/General.py53
-rw-r--r--Mailman/Gui/NonDigest.py9
-rw-r--r--Mailman/Gui/Privacy.py136
-rw-r--r--Mailman/Gui/Topics.py38
7 files changed, 265 insertions, 56 deletions
diff --git a/Mailman/Gui/Bounce.py b/Mailman/Gui/Bounce.py
index 8fb5bbc98..1dc837fc9 100644
--- a/Mailman/Gui/Bounce.py
+++ b/Mailman/Gui/Bounce.py
@@ -1,17 +1,17 @@
-# Copyright (C) 2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2004 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
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
from Mailman import mm_cfg
@@ -84,7 +84,19 @@ class Bounce(GUIBase):
('bounce_score_threshold', mm_cfg.Number, 5, 0,
_("""The maximum member bounce score before the member's
subscription is disabled. This value can be a floating point
- number.""")),
+ number."""),
+ _("""Each subscriber is assigned a bounce score, as a floating
+ point number. Whenever Mailman receives a bounce from a list
+ member, that member's score is incremented. Hard bounces (fatal
+ errors) increase the score by 1, while soft bounces (temporary
+ errors) increase the score by 0.5. Only one bounce per day
+ counts against a member's score, so even if 10 bounces are
+ received for a member on the same day, their score will increase
+ by just 1.
+
+ This variable describes the upper limit for a member's bounce
+ score, above which they are automatically disabled, but not
+ removed from the mailing list.""")),
('bounce_info_stale_after', mm_cfg.Number, 5, 0,
_("""The number of days after which a member's bounce information
diff --git a/Mailman/Gui/ContentFilter.py b/Mailman/Gui/ContentFilter.py
index 0fadf8f3e..167f42e24 100644
--- a/Mailman/Gui/ContentFilter.py
+++ b/Mailman/Gui/ContentFilter.py
@@ -1,17 +1,17 @@
-# Copyright (C) 2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2002-2005 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
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""GUI component managing the content filtering options.
@@ -57,9 +57,13 @@ class ContentFilter(GUIBase):
<p>After this initial filtering, any <tt>multipart</tt>
attachments that are empty are removed. If the outer message is
left empty after this filtering, then the whole message is
- discarded. Then, each <tt>multipart/alternative</tt> section will
+ discarded.
+
+ <p> Then, each <tt>multipart/alternative</tt> section will
be replaced by just the first alternative that is non-empty after
- filtering.
+ filtering if
+ <a href="?VARHELP=contentfilter/collapse_alternatives"
+ >collapse_alternatives</a> is enabled.
<p>Finally, any <tt>text/html</tt> parts that are left in the
message may be converted to <tt>text/plain</tt> if
@@ -74,7 +78,7 @@ class ContentFilter(GUIBase):
('filter_mime_types', mm_cfg.Text, (10, WIDTH), 0,
_("""Remove message attachments that have a matching content
type."""),
-
+
_("""Use this option to remove each message attachment that
matches one of these content types. Each line should contain a
string naming a MIME <tt>type/subtype</tt>,
@@ -100,6 +104,19 @@ class ContentFilter(GUIBase):
<tt>multipart</tt> to this list, any messages with attachments
will be rejected by the pass filter.""")),
+ ('filter_filename_extensions', mm_cfg.Text, (10, WIDTH), 0,
+ _("""Remove message attachments that have a matching filename
+ extension."""),),
+
+ ('pass_filename_extensions', mm_cfg.Text, (10, WIDTH), 0,
+ _("""Remove message attachments that don't have a matching
+ filename extension. Leave this field blank to skip this filter
+ test."""),),
+
+ ('collapse_alternatives', mm_cfg.Radio, (_('No'), _('Yes')), 0,
+ _("""Should Mailman collapse multipart/alternative to its
+ first part content?""")),
+
('convert_html_to_plaintext', mm_cfg.Radio, (_('No'), _('Yes')), 0,
_("""Should Mailman convert <tt>text/html</tt> parts to plain
text? This conversion happens after MIME attachments have been
@@ -154,10 +171,19 @@ class ContentFilter(GUIBase):
doc.addError(_('Bad MIME type ignored: %(spectype)s'))
else:
types.append(spectype.strip().lower())
- if property == 'filter_mime_types':
+ if property == 'filter_mime_types':
mlist.filter_mime_types = types
- elif property == 'pass_mime_types':
+ elif property == 'pass_mime_types':
mlist.pass_mime_types = types
+ elif property in ('filter_filename_extensions',
+ 'pass_filename_extensions'):
+ fexts = []
+ for ext in [s.strip() for s in val.splitlines()]:
+ fexts.append(ext.lower())
+ if property == 'filter_filename_extensions':
+ mlist.filter_filename_extensions = fexts
+ elif property == 'pass_filename_extensions':
+ mlist.pass_filename_extensions = fexts
else:
GUIBase._setValue(self, mlist, property, val, doc)
@@ -166,4 +192,8 @@ class ContentFilter(GUIBase):
return NL.join(mlist.filter_mime_types)
if property == 'pass_mime_types':
return NL.join(mlist.pass_mime_types)
+ if property == 'filter_filename_extensions':
+ return NL.join(mlist.filter_filename_extensions)
+ if property == 'pass_filename_extensions':
+ return NL.join(mlist.pass_filename_extensions)
return None
diff --git a/Mailman/Gui/GUIBase.py b/Mailman/Gui/GUIBase.py
index 149038926..5f0f2c1e6 100644
--- a/Mailman/Gui/GUIBase.py
+++ b/Mailman/Gui/GUIBase.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2002-2004 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
@@ -108,6 +108,8 @@ class GUIBase:
return val
if wtype == mm_cfg.Topics:
return val
+ if wtype == mm_cfg.HeaderFilter:
+ return val
# Should never get here
assert 0, 'Bad gui widget type: %s' % wtype
@@ -120,6 +122,10 @@ class GUIBase:
# Validate all the attributes for this category
pass
+ def _escape(self, property, value):
+ value = value.replace('<', '&lt;')
+ return value
+
def handleForm(self, mlist, category, subcat, cgidata, doc):
for item in self.GetConfigInfo(mlist, category, subcat):
# Skip descriptions and legacy non-attributes
@@ -138,11 +144,12 @@ class GUIBase:
elif not cgidata.has_key(property):
continue
elif isinstance(cgidata[property], ListType):
- val = [x.value for x in cgidata[property]]
+ val = [self._escape(property, x.value)
+ for x in cgidata[property]]
else:
- val = cgidata[property].value
+ val = self._escape(property, cgidata[property].value)
# Coerce the value to the expected type, raising exceptions if the
- # value is invalid
+ # value is invalid.
try:
val = self._getValidValue(mlist, property, wtype, val)
except ValueError:
diff --git a/Mailman/Gui/General.py b/Mailman/Gui/General.py
index f9fb6deca..26a55c24b 100644
--- a/Mailman/Gui/General.py
+++ b/Mailman/Gui/General.py
@@ -1,21 +1,22 @@
-# Copyright (C) 2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2005 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
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-"""MailList mixin class managing the general options.
-"""
+"""MailList mixin class managing the general options."""
+
+import re
from Mailman import mm_cfg
from Mailman import Utils
@@ -143,7 +144,11 @@ class General(GUIBase):
posted to the list, to distinguish mailing list messages in in
mailbox summaries. Brevity is premium here, it's ok to shorten
long mailing list names to something more concise, as long as it
- still identifies the mailing list.""")),
+ still identifies the mailing list.
+ You can also add a sequencial number by %%d substitution
+ directive. eg.; [listname %%d] -> [listname 123]
+ (listname %%05d) -> (listname 00123)
+ """)),
('anonymous_list', mm_cfg.Radio, (_('No'), _('Yes')), 0,
_("""Hide the sender of a message, replacing it with the list
@@ -174,7 +179,7 @@ class General(GUIBase):
messages, overriding the header in the original message if
necessary (<em>Explicit address</em> inserts the value of <a
href="?VARHELP=general/reply_to_address">reply_to_address</a>).
-
+
<p>There are many reasons not to introduce or override the
<tt>Reply-To:</tt> header. One is that some posters depend on
their own <tt>Reply-To:</tt> settings to convey their valid
@@ -183,7 +188,7 @@ class General(GUIBase):
href="http://www.unicom.com/pw/reply-to-harmful.html">`Reply-To'
Munging Considered Harmful</a> for a general discussion of this
issue. See <a
- href="http://www.metasystema.org/essays/reply-to-useful.mhtml">Reply-To
+ href="http://www.metasystema.net/essays/reply-to.mhtml">Reply-To
Munging Considered Useful</a> for a dissenting opinion.
<p>Some mailing lists have restricted posting privileges, with a
@@ -283,7 +288,7 @@ class General(GUIBase):
<li>A blank line separates paragraphs.
</ul>""")),
- ('send_welcome_msg', mm_cfg.Radio, (_('No'), _('Yes')), 0,
+ ('send_welcome_msg', mm_cfg.Radio, (_('No'), _('Yes')), 0,
_('Send welcome message to newly subscribed members?'),
_("""Turn this off only if you plan on subscribing people manually
and don't want them to know that you did so. This option is most
@@ -310,15 +315,11 @@ class General(GUIBase):
('admin_notify_mchanges', mm_cfg.Radio, (_('No'), _('Yes')), 0,
_('''Should administrator get notices of subscribes and
unsubscribes?''')),
-
+
('respond_to_post_requests', mm_cfg.Radio,
(_('No'), _('Yes')), 0,
- _('Send mail to poster when their posting is held for approval?'),
-
- _("""Approval notices are sent when mail triggers certain of the
- limits <em>except</em> routine list moderation and spam filters,
- for which notices are <em>not</em> sent. This option overrides
- ever sending the notice.""")),
+ _('Send mail to poster when their posting is held for approval?')
+ ),
_('Additional settings'),
@@ -338,7 +339,7 @@ class General(GUIBase):
# to tell if all were deselected!
0, _('''Default options for new members joining this list.<input
type="hidden" name="new_member_options" value="ignore">'''),
-
+
_("""When a new member is subscribed to this list, their initial
set of options is taken from the this variable's setting.""")),
@@ -407,6 +408,13 @@ class General(GUIBase):
headers.)"""))
)
+ # Discard held messages after this number of days
+ rtn.append(
+ ('max_days_to_hold', mm_cfg.Number, 7, 0,
+ _("""Discard held messages older than this number of days.
+ Use 0 for no automatic discarding."""))
+ )
+
return rtn
def _setValue(self, mlist, property, val, doc):
@@ -430,6 +438,15 @@ class General(GUIBase):
else:
GUIBase._setValue(self, mlist, property, val, doc)
+ def _escape(self, property, value):
+ # The 'info' property allows HTML, but lets sanitize it to avoid XSS
+ # exploits. Everything else should be fully escaped.
+ if property <> 'info':
+ return GUIBase._escape(self, property, value)
+ # Sanitize <script> and </script> tags but nothing else. Not the best
+ # solution, but expedient.
+ return re.sub(r'<([/]?script.*?)>', r'&lt;\1&gt;', value)
+
def _postValidate(self, mlist, doc):
if not mlist.reply_to_address.strip() and \
mlist.reply_goes_to_list == 2:
diff --git a/Mailman/Gui/NonDigest.py b/Mailman/Gui/NonDigest.py
index 05ed7361b..ca125a037 100644
--- a/Mailman/Gui/NonDigest.py
+++ b/Mailman/Gui/NonDigest.py
@@ -134,6 +134,15 @@ and footers:
_('''Text appended to the bottom of every immediately-delivery
message. ''') + headfoot + extra),
])
+
+ info.extend([
+ ('scrub_nondigest', mm_cfg.Toggle, (_('No'), _('Yes')), 0,
+ _('Scrub attachments of regular delivery message?'),
+ _('''When you scrub attachments, they are stored in archive
+ area and links are made in the message so that the member can
+ access via web browser. If you want the attachments totally
+ disappear, you can use content filter options.''')),
+ ])
return info
def _setValue(self, mlist, property, val, doc):
diff --git a/Mailman/Gui/Privacy.py b/Mailman/Gui/Privacy.py
index 85d5db58c..1ae2699a7 100644
--- a/Mailman/Gui/Privacy.py
+++ b/Mailman/Gui/Privacy.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2003 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
@@ -17,10 +17,19 @@
"""MailList mixin class managing the privacy options.
"""
+import re
+
from Mailman import mm_cfg
+from Mailman import Utils
from Mailman.i18n import _
from Mailman.Gui.GUIBase import GUIBase
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
class Privacy(GUIBase):
@@ -297,6 +306,12 @@ class Privacy(GUIBase):
_("""Should messages from non-members, which are automatically
discarded, be forwarded to the list moderator?""")),
+ ('nonmember_rejection_notice', mm_cfg.Text, (10, WIDTH), 1,
+ _("""Text to include in any rejection notice to be sent to
+ non-members who post to this list. This notice can include
+ the list's owner address by %%(listowner)s and replaces the
+ internally crafted default message.""")),
+
]
recip_rtn = [
@@ -361,7 +376,29 @@ class Privacy(GUIBase):
your list members end up receiving.
"""),
- _("Anti-Spam filters"),
+ _('Header filters'),
+
+ ('header_filter_rules', mm_cfg.HeaderFilter, 0, 0,
+ _('Filter rules to match against the headers of a message.'),
+
+ _("""Each header filter rule has two parts, a list of regular
+ expressions, one per line, and an action to take. Mailman
+ matches the message's headers against every regular expression in
+ the rule and if any match, the message is rejected, held, or
+ discarded based on the action you specify. Use <em>Defer</em> to
+ temporarily disable a rule.
+
+ You can have more than one filter rule for your list. In that
+ case, each rule is matched in turn, with processing stopped after
+ the first match.
+
+ Note that headers are collected from all the attachments
+ (except for the mailman administrivia message) and
+ matched against the regular expressions. With this feature,
+ you can effectively sort out messages with dangerous file
+ types or file name extensions.""")),
+
+ _('Legacy anti-spam filters'),
('bounce_matching_headers', mm_cfg.Text, (6, WIDTH), 0,
_('Hold posts with header value matching a specified regexp.'),
@@ -390,9 +427,104 @@ class Privacy(GUIBase):
return subscribing_rtn
def _setValue(self, mlist, property, val, doc):
+ # Ignore any hdrfilter_* form variables
+ if property.startswith('hdrfilter_'):
+ return
# For subscribe_policy when ALLOW_OPEN_SUBSCRIBE is true, we need to
# add one to the value because the page didn't present an open list as
# an option.
if property == 'subscribe_policy' and not mm_cfg.ALLOW_OPEN_SUBSCRIBE:
val += 1
setattr(mlist, property, val)
+
+ # We need to handle the header_filter_rules widgets specially, but
+ # everything else can be done by the base class's handleForm() method.
+ # However, to do this we need an awful hack. _setValue() and
+ # _getValidValue() will essentially ignore any hdrfilter_* form variables.
+ # TK: we should call this function only in subcat == 'spam'
+ def _handleForm(self, mlist, category, subcat, cgidata, doc):
+ # First deal with
+ rules = []
+ # We start i at 1 and keep going until we no longer find items keyed
+ # with the marked tags.
+ i = 1
+ downi = None
+ while True:
+ deltag = 'hdrfilter_delete_%02d' % i
+ reboxtag = 'hdrfilter_rebox_%02d' % i
+ actiontag = 'hdrfilter_action_%02d' % i
+ wheretag = 'hdrfilter_where_%02d' % i
+ addtag = 'hdrfilter_add_%02d' % i
+ newtag = 'hdrfilter_new_%02d' % i
+ uptag = 'hdrfilter_up_%02d' % i
+ downtag = 'hdrfilter_down_%02d' % i
+ i += 1
+ # Was this a delete? If so, we can just ignore this entry
+ if cgidata.has_key(deltag):
+ continue
+ # Get the data for the current box
+ pattern = cgidata.getvalue(reboxtag)
+ try:
+ action = int(cgidata.getvalue(actiontag))
+ # We'll get a TypeError when the actiontag is missing and the
+ # .getvalue() call returns None.
+ except (ValueError, TypeError):
+ action = mm_cfg.DEFER
+ if pattern is None:
+ # We came to the end of the boxes
+ break
+ if cgidata.has_key(newtag) and not pattern:
+ # This new entry is incomplete.
+ if i == 2:
+ # OK it is the first.
+ continue
+ doc.addError(_("""Header filter rules require a pattern.
+ Incomplete filter rules will be ignored."""))
+ continue
+ # Make sure the pattern was a legal regular expression
+ try:
+ re.compile(pattern)
+ except (re.error, TypeError):
+ safepattern = Utils.websafe(pattern)
+ doc.addError(_("""The header filter rule pattern
+ '%(safepattern)s' is not a legal regular expression. This
+ rule will be ignored."""))
+ continue
+ # Was this an add item?
+ if cgidata.has_key(addtag):
+ # Where should the new one be added?
+ where = cgidata.getvalue(wheretag)
+ if where == 'before':
+ # Add a new empty rule box before the current one
+ rules.append(('', mm_cfg.DEFER, True))
+ rules.append((pattern, action, False))
+ # Default is to add it after...
+ else:
+ rules.append((pattern, action, False))
+ rules.append(('', mm_cfg.DEFER, True))
+ # Was this an up movement?
+ elif cgidata.has_key(uptag):
+ # As long as this one isn't the first rule, move it up
+ if rules:
+ rules.insert(-1, (pattern, action, False))
+ else:
+ rules.append((pattern, action, False))
+ # Was this the down movement?
+ elif cgidata.has_key(downtag):
+ downi = i - 2
+ rules.append((pattern, action, False))
+ # Otherwise, just retain this one in the list
+ else:
+ rules.append((pattern, action, False))
+ # Move any down button filter rule
+ if downi is not None:
+ rule = rules[downi]
+ del rules[downi]
+ rules.insert(downi+1, rule)
+ mlist.header_filter_rules = rules
+
+ def handleForm(self, mlist, category, subcat, cgidata, doc):
+ if subcat == 'spam':
+ self._handleForm(mlist, category, subcat, cgidata, doc)
+ # Everything else is dealt with by the base handler
+ GUIBase.handleForm(self, mlist, category, subcat, cgidata, doc)
diff --git a/Mailman/Gui/Topics.py b/Mailman/Gui/Topics.py
index 494c76517..e25ef65d3 100644
--- a/Mailman/Gui/Topics.py
+++ b/Mailman/Gui/Topics.py
@@ -1,26 +1,33 @@
-# Copyright (C) 2001,2002 by the Free Software Foundation, Inc.
+# Copyright (C) 2001-2003 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
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
import re
from Mailman import mm_cfg
+from Mailman import Utils
from Mailman.i18n import _
from Mailman.Logging.Syslog import syslog
from Mailman.Gui.GUIBase import GUIBase
+try:
+ True, False
+except NameError:
+ True = 1
+ False = 0
+
class Topics(GUIBase):
@@ -88,7 +95,7 @@ class Topics(GUIBase):
# We start i at 1 and keep going until we no longer find items keyed
# with the marked tags.
i = 1
- while 1:
+ while True:
deltag = 'topic_delete_%02d' % i
boxtag = 'topic_box_%02d' % i
reboxtag = 'topic_rebox_%02d' % i
@@ -96,51 +103,46 @@ class Topics(GUIBase):
wheretag = 'topic_where_%02d' % i
addtag = 'topic_add_%02d' % i
newtag = 'topic_new_%02d' % i
-
i += 1
# Was this a delete? If so, we can just ignore this entry
if cgidata.has_key(deltag):
continue
-
# Get the data for the current box
name = cgidata.getvalue(boxtag)
pattern = cgidata.getvalue(reboxtag)
desc = cgidata.getvalue(desctag)
-
if name is None:
# We came to the end of the boxes
break
-
if cgidata.has_key(newtag) and (not name or not pattern):
# This new entry is incomplete.
doc.addError(_("""Topic specifications require both a name and
a pattern. Incomplete topics will be ignored."""))
continue
-
# Make sure the pattern was a legal regular expression
+ name = Utils.websafe(name)
try:
re.compile(pattern)
except (re.error, TypeError):
- doc.addError(_("""The topic pattern `%(pattern)s' is not a
+ safepattern = Utils.websafe(pattern)
+ doc.addError(_("""The topic pattern '%(safepattern)s' is not a
legal regular expression. It will be discarded."""))
continue
-
# Was this an add item?
if cgidata.has_key(addtag):
# Where should the new one be added?
where = cgidata.getvalue(wheretag)
if where == 'before':
# Add a new empty topics box before the current one
- topics.append(('', '', '', 1))
- topics.append((name, pattern, desc, 0))
+ topics.append(('', '', '', True))
+ topics.append((name, pattern, desc, False))
# Default is to add it after...
else:
- topics.append((name, pattern, desc, 0))
- topics.append(('', '', '', 1))
+ topics.append((name, pattern, desc, False))
+ topics.append(('', '', '', True))
# Otherwise, just retain this one in the list
else:
- topics.append((name, pattern, desc, 0))
-
+ topics.append((name, pattern, desc, False))
# Add these topics to the mailing list object, and deal with other
# options.
mlist.topics = topics