## ## phonewidget.py ## $Id$ ## """A phone number widget. This widget is an input widget (not made for display forms but for edit and add forms). It can be used for :class:`zope.schema.TextLine` fields but has to be requested by the form manually (otherwise the regular TextLine widget will be used for rendering). The code was mainly taken from usphone implementation in z3c.widget (which was designed for outdated zope.app.form (instead of zope.formlib or newer form libs). Therefore some modifications were neccessary and it might look a bit overloaded. """ import re from waeup.sirp import MessageFactory as _ from zope import schema from zope.browserpage import ViewPageTemplateFile from zope.component import getMultiAdapter from zope.formlib import form from zope.formlib.interfaces import IBrowserWidget, IWidgetInputErrorView from zope.formlib.interfaces import IInputWidget, WidgetInputError from zope.formlib.widget import SimpleInputWidget from zope.interface import Interface, implements class IPhoneData(Interface): """A schema used to generate a Phone widget.""" country = schema.TextLine( title=_('Country Code'), description=_('The country code of the phone number.'), min_length=1, constraint=re.compile(r'^[0-9]+$').search, required=True) area = schema.TextLine( title=_('Area Code'), description=_('The area code of the phone number.'), min_length=1, constraint=re.compile(r'^[0-9]+$').search, required=True) extension = schema.TextLine( title=_('Direct Line'), description=_('The direct line of the phone number.'), min_length=3, constraint=re.compile(r'^[0-9]{3,}$').search, required=True) class PhoneWidgetData(object): """Phone number data""" implements(IPhoneData) country = None area = None extension = None def __init__(self, context, number=None): self.context = context if number is not None: # XXX: We have to provide something more tolerant self.country, self.area, self.extension = number.split('-') return try: self.country, self.area, self.extension = number.split('-') except ValueError: replacement = '1-1-%s' % (number.replace('-', ''),) self.country, self.area, self.extension = replacement.split( '-', 2) @property def number(self): return u'-'.join((self.country, self.area, self.extension)) class PhoneWidget(SimpleInputWidget): """Phone Number Widget""" implements(IBrowserWidget, IInputWidget) template = ViewPageTemplateFile('phonewidget.pt') _prefix = 'field.' _error = None widgets = {} # See zope.formlib.interfaces.IWidget name = None visible = True def __init__(self, field, request): super(PhoneWidget, self).__init__(field, request) value = field.query(field.context) self._generateSubWidgets() return def _generateSubWidgets(self): """Create the three subwidgets. """ value = self.context.query(self.context.context) adapters = {} adapters[IPhoneData] = PhoneWidgetData(self, value) self.widgets = form.setUpEditWidgets( form.FormFields(IPhoneData), self.name, value, self.request, adapters=adapters) self.widgets['country'].displayWidth = 3 self.widgets['area'].displayWidth = 5 self.widgets['extension'].displayWidth = 10 return def setRenderedValue(self, value): """See zope.formlib.interfaces.IWidget""" if isinstance(value, unicode): country, area, extension = value.split('-') self.widgets['country'].setRenderedValue(country) self.widgets['area'].setRenderedValue(area) self.widgets['extension'].setRenderedValue(extension) def setPrefix(self, prefix): """See zope.formlib.interfaces.IWidget""" # Set the prefix locally if not prefix.endswith("."): prefix += '.' self._prefix = prefix self.name = prefix + self.context.__name__ # Now distribute it to the sub-widgets self._generateSubWidgets() return def getInputValue(self): """See zope.formlib.interfaces.IInputWidget""" self._error = None try: return u'-'.join(( self.widgets['country'].getInputValue(), self.widgets['area'].getInputValue(), self.widgets['extension'].getInputValue() )) except ValueError, v: self._error = WidgetInputError( self.context.__name__, self.label, _(v)) raise self._error except WidgetInputError, e: self._error = e raise e def applyChanges(self, content): """See zope.formlib.interfaces.IInputWidget""" field = self.context new_value = self.getInputValue() old_value = field.query(content, self) # The selection has not changed if new_value == old_value: return False field.set(content, new_value) return True def hasInput(self): """See zope.formlib.interfaces.IInputWidget""" return (self.widgets['country'].hasInput() and (self.widgets['area'].hasInput() and self.widgets['extension'].hasInput())) def hasValidInput(self): """See zope.formlib.interfaces.IInputWidget""" return (self.widgets['country'].hasValidInput() and self.widgets['area'].hasValidInput() and self.widgets['extension'].hasValidInput()) def hidden(self): """See zope.formlib.interfaces.IBrowserWidget""" output = [] output.append(self.widgets['country'].hidden()) output.append(self.widgets['area'].hidden()) output.append(self.widgets['extension'].hidden()) return '\n'.join(output) def error(self): """See zope.formlib.interfaces.IBrowserWidget""" if self._error: return getMultiAdapter( (self._error, self.request), IWidgetInputErrorView).snippet() country_error = self.widgets['country'].error() if country_error: return country_error area_error = self.widgets['area'].error() if area_error: return area_error extension_error = self.widgets['extension'].error() if extension_error: return extension_error return "" def __call__(self): """See zope.formlib.interfaces.IBrowserWidget""" return self.template()