from types import DictionaryType, ListType
import string
try:
from WebUtils.Funcs import htmlEncode
except ImportError:
from Webware.WebUtils.Funcs import htmlEncode
from BaseValidatorClasses import InvalidField
from BaseTagClasses import HTMLElement
from BaseChoiceClasses import ChoiceList
class MethodUndefined(Exception):
pass
class Field(HTMLElement):
def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
'''The bulk of Field's functionality derives from the code in HTMLElement. In addition,
the Field constructor sets up a bunch of additional containers; validators is a list of
validator/converter instances; the label is a string that is used by some field classes, but not
others, mainly for things like button labels; attributes is a dictionary of parameters that will
be added to the field's HTML tag; languageStandard is whether you want HTML or XHTML tags returned'''
HTMLElement.__init__(self,name,label,attributes,languageStandard)
self._validators = validators
self._default = None
self._rawValue = None
self._error = None
def setDefault(self, value):
'''Public method to directly set the default value of the field'''
self._default = value
def _processDefaults(self, dict):
'''Any value in dict whose key matches field name will be chosen as the new default value'''
default = dict.get( self.name(), None)
if default is not None:
self._default = default
def _processRawValues(self, dict):
'''Mechanism for processing user submission through a field object's validator/converters'''
hasError = None
value = self._findSelfInDict(dict)
self._rawValue = value
try:
self._value = value
for validator in self._validators:
self._value = validator._attemptConvert(value)
self._error = None
except InvalidField, msg:
self._error = str(msg)
self._value = None
self.addClass('error')
def _setValue(self, value):
'''Sets the current value of the field element'''
self._value = value
def _findSelfInDict(self, dict):
'''looks through the passed in dict, trying to find self.name()'''
return dict.get( self.name() )
def _reset(self):
'''Sets the value, default, and error to None'''
self._value = self._default = self._error = None
try:
self._styleClass.remove('error')
except ValueError:
pass
def _generateID(self):
'''Private utility method for generating id attributes for tags'''
if self._idAttribute:
return self._idAttribute
else:
return 'field_%s' % self.name()
def error(self):
'''Public method to return any error message resulting from validation (returns None if no error)'''
return self._error
def default(self):
'''Public method to return the default value for this field (if any)'''
return self._default
def validators(self):
'''Public method to return the list of validators assigned to this field (if any)'''
return self._validators
def addValidator(self,validator):
'''Public method to add a validator instance to this field'''
self._validators.append(validator)
def clearValidators(self):
'''Public method to remove all validators from this field'''
self._validators = []
def _valueForDisplay(self):
'''Private method that returns the most appropriate value for form rendering.'''
if self.default() is not None and self._value is None:
val = self._default
else:
if self._value is None:
val = ''
else:
val = self._value
if self._error and self._rawValue:
val = self._rawValue
return val
def dump(self):
'''Public method for returning a generic lump of HTML to represent the field'''
error = self.error()
if error is None:
error = ""
else:
error += "<br%s>" % self._closeTag()
return "%s%s" % ( error, HTMLElement.dump(self) )
def tag(self):
'''Public method for returning an <input> tag and nothing but an <input> tag; except when it doesn't, such as
with radio button sets where the radio switches mean nothing without their labels'''
raise MethodUndefined, '''Ooops! No tag method defined for this abstract field class!'''
class ChooseField(Field):
'''Subclass of field that accomodates Field types that are for multiple choice form elements:
Select boxes, multi-select boxes, radio sets, checkbox sets'''
def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
Field.__init__(self, name, validators, label, attributes, languageStandard)
self._choiceList = ChoiceList(type='select')
self._choiceList._setName( name )
id = self._generateID()
self._choiceList._setParentID( id )
def _processDefaults(self, dict):
'''need special cases of these to set the "selected" state in the ChoiceList'''
Field._processDefaults(self,dict)
self._choiceList.setSelected(self._default)
def _processRawValues(self, dict):
'''need special cases of these to set the "selected" state in the ChoiceList'''
Field._processRawValues(self,dict)
self._choiceList.setSelected(self._rawValue)
def _reset(self):
'''Sets the value, default, and error to None'''
Field._reset(self)
self._choiceList._reset()
def setID(self,string):
'''Public utility method to surgically override the id tag attribute'''
Field.setID(self,string)
self._choiceList._setParentID(string)
def tag(self):
'''Public method to return the HTML tags for this field'''
raise MethodUndefined, '''Ooops! No tag method defined for this abstract field class!'''
def choices(self):
'''Public method to return the choice class instances that are in this select field'''
return self._choiceList.choices()
def choiceList(self):
'''Public method to return the ChoiceList class instance (for access to its methods)'''
return self._choiceList
def addChoice(self, value, label):
'''Pass in a value, and text label to add a selection to this ChoiceList'''
return self._choiceList.addChoice(value, label)
def setChoices(self,choiceListObject):
'''Pass a selectionList object to this field object to set its selection set'''
self._choiceList = choiceListObject
self._choiceList._setName( self.name() )
self._choiceList._setParentID( self.id() )
self._choiceList.setType( 'select' )
def clearChoices(self):
self._choiceList.clear()
class CompoundField(Field):
'''Abstract Subclass of Field that implements subfields, and modifies the behavior of the methods that Form uses to mess with fields
to accomodate subFields.'''
def __init__(self, name, validators=[], label='', attributes={}, languageStandard='XHTML'):
Field.__init__(self,name,validators,label,attributes,languageStandard)
self._subFields = [ ]
self._derivedValue = None
def addSubField( self, field ):
'''adds a field class instance to subfields'''
self._subFields.append( field )
def value( self ):
'''access method that returns the 'value' of the compound set, NOT
the value of the main form field. This value is usually derived from
some combination of the field and its' subFields, or some other logic'''
return self._derivedValue
def nativeValue( self ):
'''the same as .value() if this were a non-compound field class'''
return Field.value(self)
def subFields(self):
'''returns a list of the sub fields'''
return self._subFields
def _processRawValues(self,dict):
'''may have to override (usually will) for compound fields... for special app logic'''
if hasattr(self,'choices'):
ChooseField._processRawValues(self,dict)
else:
Field._processRawValues(self,dict)
for each in self._subFields:
each._processRawValues(dict)
self._deriveValue()
def _deriveValue(self):
'''figures out what the compound value should be... the value of the set'''
self._derivedValue = self.nativeValue()
def _processDefaults(self, dict):
if hasattr(self,'choices'):
ChooseField._processDefaults(self, dict)
else:
Field._processDefaults(self, dict)
for each in self._subFields:
each._processDefaults(dict)
def _reset(self):
if hasattr(self,'choices'):
ChooseField._reset(self)
else:
Field._reset(self)
for each in self._subFields:
each._reset()
self._derivedValue = None
def error(self):
'''Returns validation errors, allowing for the errors of the CompoundField's sub-fields'''
errors = []
if Field.error(self):
errors.append( Field.error(self) )
for each in self._subFields:
if each.error():
errors.append( each.error() )
return string.join(errors, ", ")
def tag(self):
'''This isn't terribly useful as-is; you may want to make it more so in the sub-class'''
tags = []
try:
tags.append( Field.tag(self) )
except MethodUndefined:
pass
for each in self._subFields:
tags.append( each.tag() )
return string.join(tags, "<br%s>\n" % self._closeTag() )
def dump(self):
'''This isn't terribly useful as-is; you may want to make it more so in the sub-class'''
tmpStr = ""
tmpStr += Field.dump(self)
for each in self._subFields:
tmpStr += each.dump()
return tmpStr