source: main/waeup.kofa/branches/uli-rm-bootstrap/src/waeup/kofa/schema/field.py @ 17952

Last change on this file since 17952 was 8171, checked in by uli, 13 years ago

Add PhoneNumber? schema field and register PhoneWidget? as default edit widget for it.

  • Property svn:keywords set to Id
File size: 6.1 KB
Line 
1## $Id: field.py 8171 2012-04-16 07:49:22Z uli $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18"""Special fields.
19"""
20from zope.interface import implements
21from zope.schema import TextLine, Date
22from zope.schema.interfaces import (
23    ITextLine, IBaseVocabulary, ISource, IContextSourceBinder, InvalidValue,)
24from zope.schema.vocabulary import (
25    SimpleVocabulary, getVocabularyRegistry, VocabularyRegistryError)
26from waeup.kofa.schema.interfaces import IFormattedDate, IPhoneNumber
27
28class CustomizableErrorMsg(object):
29    # Still work in progress
30
31    def __init__(self, not_in_vocab=InvalidValue, **kw):
32        self.not_in_vocab = not_in_vocab
33        return
34
35class TextLineChoice(TextLine, CustomizableErrorMsg):
36    """A TextLine field that also accepts sources, vocabs and values.
37
38    You can use this field like a regular zope.schema.TextLine field.
39
40    You can additionally give `source`, `values` or `vocabulary`
41    parameters which will work mainly as with regular
42    zope.schema.Choice fields (hence the name). Different to
43    `Choice` field, these parameters are not mandatory. Leave them
44    out and you have a regular `TextLine` field.
45
46    If you pass a simple source parameter, only those values are
47    allowed which also appear in the source. The source should of
48    course provide string values as suitable for TextLine fields only!
49
50    If, for example you create a TextLineChoice like this:
51
52      name = TextLineChoice(
53        title = u'Some name',
54        values = [u'foo', u'bar']  # unicode allowed only!
55        )
56
57    any formlib form rendering this field will only accept input u'foo'
58    or u'bar'.
59
60    The main advantage of this modified TextLine field is to support
61    contextual sources. That means you can define some
62    IContextSourceBinder component that looks up catalogs or something
63    external else and decide then, whether the entered value of a form
64    is allowed or not.
65
66    The code herein is mainly copied over from
67    zope.schema._field.Choice with slight modifications.
68    """
69    implements(ITextLine)
70
71    def __init__(self, values=None, vocabulary=None, source=None, **kw):
72        if vocabulary is not None:
73            assert (isinstance(vocabulary, basestring)
74                    or IBaseVocabulary.providedBy(vocabulary))
75            assert source is None, (
76                "You cannot specify both source and vocabulary.")
77        elif source is not None:
78            vocabulary = source
79
80        assert values is None or vocabulary is None, (
81               "You cannot specify both values and vocabulary.")
82
83        self.vocabulary = None
84        self.vocabularyName = None
85        if values is not None:
86            self.vocabulary = SimpleVocabulary.fromValues(values)
87        elif isinstance(vocabulary, (unicode, str)):
88            self.vocabularyName = vocabulary
89        elif (ISource.providedBy(vocabulary) or
90              IContextSourceBinder.providedBy(vocabulary)):
91            self.vocabulary = vocabulary
92        super(TextLineChoice, self).__init__(**kw)
93        return
94
95    source = property(lambda self: self.vocabulary)
96
97    def bind(self, object):
98        """See zope.schema._bootstrapinterfaces.IField."""
99        clone = super(TextLineChoice, self).bind(object)
100        # get registered vocabulary if needed:
101        if IContextSourceBinder.providedBy(self.vocabulary):
102            clone.vocabulary = self.vocabulary(object)
103            assert ISource.providedBy(clone.vocabulary)
104        elif clone.vocabulary is None and self.vocabularyName is not None:
105            vr = getVocabularyRegistry()
106            clone.vocabulary = vr.get(object, self.vocabularyName)
107            assert ISource.providedBy(clone.vocabulary)
108        return clone
109
110    def _validate(self, value):
111        """First validate against the regular TextLine rules. Then check any
112        vocabularies/sources.
113        """
114        # Do TextLine validation
115        super(TextLineChoice, self)._validate(value)
116
117        # Check allowed value range (the vocabulary part)
118        vocabulary = self.vocabulary
119        if vocabulary is None and self.vocabularyName is not None:
120            vr = getVocabularyRegistry()
121            try:
122                vocabulary = vr.get(None, self.vocabularyName)
123            except VocabularyRegistryError:
124                raise ValueError("Can't validate value without vocabulary")
125        if vocabulary and value not in vocabulary:
126            raise InvalidValue(value)
127        return
128
129class FormattedDate(Date):
130    """A date field that supports additional formatting attributes.
131
132    Stores extra attributes (see below). To make use of these
133    attributes in forms, you have to provide widgets that read them
134    and use them in their operations, for instance the
135    `waeup.kofa.widgets.datewidget.FormattedDateWidget`.
136
137    Extra attributes are as follows:
138
139    `date_format`
140      additional attribute to describe desired date format. Must be a
141      string that can be fed to strftime/strptime functions. By
142      default `None`.
143
144    `show_year`
145      boolean indicating whether some kind of year selection should
146      be used with this instance. `False` by default.
147    """
148    implements(IFormattedDate)
149    date_format = None
150    show_year = False
151    def __init__(self, date_format=None, show_year=False, *args, **kw):
152        self.date_format = date_format
153        self.show_year = show_year
154        return super(FormattedDate, self).__init__(*args, **kw)
155
156class PhoneNumber(TextLine):
157    """A schema field for phone numbers.
158    """
159    implements(IPhoneNumber)
Note: See TracBrowser for help on using the repository browser.