source: main/waeup.sirp/trunk/src/waeup/sirp/widgets/phonewidget.py @ 7415

Last change on this file since 7415 was 7415, checked in by Henrik Bettermann, 13 years ago
  • Property svn:keywords set to Id
File size: 7.9 KB
Line 
1##
2## phonewidget.py
3## $Id: phonewidget.py 7415 2011-12-21 07:15:47Z henrik $
4##
5"""A phone number widget.
6
7This widget is an input widget (not made for display forms but for
8edit and add forms).
9
10It can be used for :class:`zope.schema.TextLine` fields but has to be
11requested by the form manually (otherwise the regular TextLine widget
12will be used for rendering).
13
14The code was mainly taken from usphone implementation in z3c.widget
15(which was designed for outdated zope.app.form (instead of
16zope.formlib or newer form libs). Therefore some modifications were
17neccessary and it might look a bit overloaded.
18
19If you use the PhoneWidget for rendering regular TextLine attributes
20(preferably in edit forms or add forms), the phone number is displayed
21by three input fields representing the international code, the area
22code and the extension line. All three fields require pure numbers as
23input and do not accept other chars.
24
25When the entered input is stored with a context object, it is stored
26as a single unicode string with the numbers divided by single hyphen.
27
28So, input <12>, <111>, <444> becomes the string ``'12-111-444'`` for
29the context object.
30
31"""
32import re
33from waeup.sirp import MessageFactory as _
34from zope import schema
35from zope.browserpage import ViewPageTemplateFile
36from zope.component import getMultiAdapter
37from zope.formlib import form
38from zope.formlib.interfaces import (
39    IBrowserWidget, IWidgetInputErrorView, IInputWidget, WidgetInputError,
40    MissingInputError)
41from zope.formlib.widget import SimpleInputWidget
42from zope.interface import Interface, implements
43from zope.schema.interfaces import RequiredMissing
44
45class IPhoneData(Interface):
46    """A schema used to generate a Phone widget."""
47
48    country = schema.TextLine(
49        title=_('Country Code'),
50        description=_('The country code of the phone number.'),
51        min_length=1,
52        constraint=re.compile(r'^[0-9]+$').search,
53        default=u'234', # int. code for nigeria
54        required=True)
55
56    area = schema.TextLine(
57        title=_('Area Code'),
58        description=_('The area code of the phone number.'),
59        min_length=1,
60        constraint=re.compile(r'^[0-9]+$').search,
61        required=True)
62
63    extension = schema.TextLine(
64        title=_('Direct Line'),
65        description=_('The direct line of the phone number.'),
66        min_length=3,
67        constraint=re.compile(r'^[0-9]{3,}$').search,
68        required=True)
69
70
71class PhoneWidgetData(object):
72    """Phone number data"""
73    implements(IPhoneData)
74
75    country = None
76    area = None
77    extension = None
78
79    def __init__(self, context, number=None):
80        """Set countrycode, areacode, and extension line from number.
81        """
82        self.context = context
83        if number is None:
84            return
85        # handle other types than strings.
86        if not isinstance(number, basestring):
87            number = str(number)
88        parts = number.split('-', 2)
89        if len(parts) == 2:
90            parts = [None,] + parts
91        elif len(parts) == 0:
92            return
93        elif len(parts) == 1:
94            parts = [None, None] + parts
95        self.country, self.area, self.extension = parts
96        return
97
98    @property
99    def number(self):
100        """Return a valid phone number string of format ``<IC>-<AC>-<EL>``.
101
102        where <IC> is the country code (digits only), <AC> is the area
103        code and <EL> is the extension line.
104        """
105        return u'-'.join(
106            (self.country or '', self.area or '', self.extension))
107
108
109class PhoneWidget(SimpleInputWidget):
110    """Phone Number Widget"""
111    implements(IBrowserWidget, IInputWidget)
112
113    template = ViewPageTemplateFile('phonewidget.pt')
114    _prefix = 'field.'
115    _error = None
116    widgets = {}
117
118    # See zope.formlib.interfaces.IWidget
119    name = None
120    visible = True
121
122    def __init__(self, field, request):
123        super(PhoneWidget, self).__init__(field, request)
124        value = field.query(field.context)
125        self._generateSubWidgets()
126        return
127
128    def _generateSubWidgets(self):
129        """Create the three subwidgets.
130        """
131        value = self.context.query(self.context.context)
132        adapters = {}
133        adapters[IPhoneData] = PhoneWidgetData(self, value)
134        self.widgets = form.setUpEditWidgets(
135            form.FormFields(IPhoneData),
136            self.name, value, self.request, adapters=adapters)
137        self.widgets['country'].displayWidth = 3
138        self.widgets['area'].displayWidth = 5
139        self.widgets['extension'].displayWidth = 10
140        return
141
142    def setRenderedValue(self, value):
143        """See zope.formlib.interfaces.IWidget"""
144        if isinstance(value, unicode):
145            country, area, extension = value.split('-')
146            self.widgets['country'].setRenderedValue(country)
147            self.widgets['area'].setRenderedValue(area)
148            self.widgets['extension'].setRenderedValue(extension)
149
150
151    def setPrefix(self, prefix):
152        """See zope.formlib.interfaces.IWidget"""
153        # Set the prefix locally
154        if not prefix.endswith("."):
155            prefix += '.'
156        self._prefix = prefix
157        self.name = prefix + self.context.__name__
158        # Now distribute it to the sub-widgets
159        self._generateSubWidgets()
160        return
161
162    def getInputValue(self):
163        """See zope.formlib.interfaces.IInputWidget"""
164        self._error = None
165        try:
166            return u'-'.join((
167                self.widgets['country'].getInputValue(),
168                self.widgets['area'].getInputValue(),
169                self.widgets['extension'].getInputValue() ))
170        except ValueError, v:
171            self._error = WidgetInputError(
172                self.context.__name__, self.label, _(v))
173            raise self._error
174        except WidgetInputError, e:
175            # Subwidgets require input. We have to work around that if
176            # the parent widget does not require input.
177            missing = isinstance(e.errors, RequiredMissing)
178            missing = missing or isinstance(e, MissingInputError)
179            if not self.required and missing:
180                return self._missing
181            self._error = e
182            raise e
183
184    def applyChanges(self, content):
185        """See zope.formlib.interfaces.IInputWidget"""
186        field = self.context
187        new_value = self.getInputValue()
188        old_value = field.query(content, self)
189        # The selection has not changed
190        if new_value == old_value:
191            return False
192        field.set(content, new_value)
193        return True
194
195    def hasInput(self):
196        """See zope.formlib.interfaces.IInputWidget"""
197        return (self.widgets['country'].hasInput() and
198                (self.widgets['area'].hasInput() and
199                 self.widgets['extension'].hasInput()))
200
201
202    def hasValidInput(self):
203        """See zope.formlib.interfaces.IInputWidget"""
204        return (self.widgets['country'].hasValidInput() and
205            self.widgets['area'].hasValidInput() and
206            self.widgets['extension'].hasValidInput())
207
208
209    def hidden(self):
210        """See zope.formlib.interfaces.IBrowserWidget"""
211        output = []
212        output.append(self.widgets['country'].hidden())
213        output.append(self.widgets['area'].hidden())
214        output.append(self.widgets['extension'].hidden())
215        return '\n'.join(output)
216
217
218    def error(self):
219        """See zope.formlib.interfaces.IBrowserWidget"""
220        if self._error:
221            return getMultiAdapter(
222                (self._error, self.request),
223                IWidgetInputErrorView).snippet()
224        country_error = self.widgets['country'].error()
225        if country_error:
226            return country_error
227        area_error = self.widgets['area'].error()
228        if area_error:
229            return area_error
230        extension_error = self.widgets['extension'].error()
231        if extension_error:
232            return extension_error
233        return ""
234
235    def __call__(self):
236        """See zope.formlib.interfaces.IBrowserWidget"""
237        return self.template()
Note: See TracBrowser for help on using the repository browser.