/usr/lib/mailman/Mailman/Gui/GUIBase.py is in mailman 1:2.1.16-2.
This file is owned by root:list, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 | # Copyright (C) 2002-2008 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.
"""Base class for all web GUI components."""
import re
from types import TupleType, ListType
from Mailman import mm_cfg
from Mailman import Utils
from Mailman import Errors
from Mailman.i18n import _
NL = '\n'
BADJOINER = '</code>, <code>'
class GUIBase:
# Providing a common interface for GUI component form processing. Most
# GUI components won't need to override anything, but some may want to
# override _setValue() to provide some specialized processing for some
# attributes.
def _getValidValue(self, mlist, property, wtype, val):
# Coerce and validate the new value.
#
# Radio buttons and boolean toggles both have integral type
if wtype in (mm_cfg.Radio, mm_cfg.Toggle):
# Let ValueErrors propagate
return int(val)
# String and Text widgets both just return their values verbatim
if wtype in (mm_cfg.String, mm_cfg.Text):
return val
# This widget contains a single email address
if wtype == mm_cfg.Email:
# BAW: We must allow blank values otherwise reply_to_address can't
# be cleared. This is currently the only mm_cfg.Email type widget
# in the interface, so watch out if we ever add any new ones.
if val:
# Let MMBadEmailError and MMHostileAddress propagate
Utils.ValidateEmail(val)
return val
# These widget types contain lists of email addresses, one per line.
# The EmailListEx allows each line to contain either an email address
# or a regular expression
if wtype in (mm_cfg.EmailList, mm_cfg.EmailListEx):
# BAW: value might already be a list, if this is coming from
# config_list input. Sigh.
if isinstance(val, ListType):
return val
addrs = []
bad_addrs = []
for addr in [s.strip() for s in val.split(NL)]:
# Discard empty lines
if not addr:
continue
try:
# This throws an exception if the address is invalid
Utils.ValidateEmail(addr)
except Errors.EmailAddressError:
# See if this is a context that accepts regular
# expressions, and that the re is legal
if wtype == mm_cfg.EmailListEx and addr.startswith('^'):
try:
re.compile(addr)
except re.error:
bad_addrs.append(addr)
elif (wtype == mm_cfg.EmailListEx and addr.startswith('@')
and property.endswith('_these_nonmembers')):
# XXX Needs to be reviewed for list@domain names.
# don't reference your own list
if addr[1:] == mlist.internal_name():
bad_addrs.append(addr)
# check for existence of list? For now allow
# reference to list before creating it.
else:
bad_addrs.append(addr)
if property in ('regular_exclude_lists',
'regular_include_lists'):
if addr.lower() == mlist.GetListEmail().lower():
bad_addrs.append(addr)
addrs.append(addr)
if bad_addrs:
raise Errors.EmailAddressError, ', '.join(bad_addrs)
return addrs
# This is a host name, i.e. verbatim
if wtype == mm_cfg.Host:
return val
# This is a number, either a float or an integer
if wtype == mm_cfg.Number:
# The int/float code below doesn't work if we are called from
# config_list with a value that is already a float. It will
# truncate the value to an int.
if isinstance(val, float):
return val
num = -1
try:
num = int(val)
except ValueError:
# Let ValueErrors percolate up
num = float(val)
if num < 0:
return getattr(mlist, property)
return num
# This widget is a select box, i.e. verbatim
if wtype == mm_cfg.Select:
return val
# Checkboxes return a list of the selected items, even if only one is
# selected.
if wtype == mm_cfg.Checkbox:
if isinstance(val, ListType):
return val
return [val]
if wtype == mm_cfg.FileUpload:
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
def _setValue(self, mlist, property, val, doc):
# Set the value, or override to take special action on the property
if not property.startswith('_') and getattr(mlist, property) <> val:
setattr(mlist, property, val)
def _postValidate(self, mlist, doc):
# Validate all the attributes for this category
pass
def handleForm(self, mlist, category, subcat, cgidata, doc):
for item in self.GetConfigInfo(mlist, category, subcat):
# Skip descriptions and legacy non-attributes
if not isinstance(item, TupleType) or len(item) < 5:
continue
# Unpack the gui item description
property, wtype, args, deps, desc = item[0:5]
# BAW: I know this code is a little crufty but I wanted to
# reproduce the semantics of the original code in admin.py as
# closely as possible, for now. We can clean it up later.
#
# The property may be uploadable...
uploadprop = property + '_upload'
if cgidata.has_key(uploadprop) and cgidata[uploadprop].value:
val = cgidata[uploadprop].value
elif not cgidata.has_key(property):
continue
elif isinstance(cgidata[property], ListType):
val = [x.value for x in cgidata[property]]
else:
val = cgidata[property].value
# Coerce the value to the expected type, raising exceptions if the
# value is invalid.
try:
val = self._getValidValue(mlist, property, wtype, val)
except ValueError:
doc.addError(_('Invalid value for variable: %(property)s'))
# This is the parent of MMBadEmailError and MMHostileAddress
except Errors.EmailAddressError, error:
doc.addError(
_('Bad email address for option %(property)s: %(error)s'))
else:
# Set the attribute, which will normally delegate to the mlist
self._setValue(mlist, property, val, doc)
# Do a final sweep once all the attributes have been set. This is how
# we can do cross-attribute assertions
self._postValidate(mlist, doc)
# Convenience method for handling $-string attributes
def _convertString(self, mlist, property, alloweds, val, doc):
# Is the list using $-strings?
dollarp = getattr(mlist, 'use_dollar_strings', 0)
if dollarp:
ids = Utils.dollar_identifiers(val)
else:
# %-strings
ids = Utils.percent_identifiers(val)
# Here's the list of allowable interpolations
for allowed in alloweds:
if ids.has_key(allowed):
del ids[allowed]
if ids:
# What's left are not allowed
badkeys = ids.keys()
badkeys.sort()
bad = BADJOINER.join(badkeys)
doc.addError(_(
"""The following illegal substitution variables were
found in the <code>%(property)s</code> string:
<code>%(bad)s</code>
<p>Your list may not operate properly until you correct this
problem."""), tag=_('Warning: '))
return val
# Now if we're still using %-strings, do a roundtrip conversion and
# see if the converted value is the same as the new value. If not,
# then they probably left off a trailing `s'. We'll warn them and use
# the corrected string.
if not dollarp:
fixed = Utils.to_percent(Utils.to_dollar(val))
if fixed <> val:
doc.addError(_(
"""Your <code>%(property)s</code> string appeared to
have some correctable problems in its new value.
The fixed value will be used instead. Please
double check that this is what you intended.
"""))
return fixed
return val
|