aboutsummaryrefslogtreecommitdiffstats
path: root/Mailman/Gui/GUIBase.py
diff options
context:
space:
mode:
Diffstat (limited to 'Mailman/Gui/GUIBase.py')
-rw-r--r--Mailman/Gui/GUIBase.py200
1 files changed, 200 insertions, 0 deletions
diff --git a/Mailman/Gui/GUIBase.py b/Mailman/Gui/GUIBase.py
new file mode 100644
index 00000000..7062235e
--- /dev/null
+++ b/Mailman/Gui/GUIBase.py
@@ -0,0 +1,200 @@
+# Copyright (C) 2002 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 = []
+ 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:
+ raise ValueError
+ else:
+ raise
+ addrs.append(addr)
+ 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:
+ 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
+ # 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:
+ doc.addError(
+ _('Bad email address for option %(property)s: %(val)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