source: main/waeup.kofa/trunk/src/waeup/kofa/utils/converters.py @ 7856

Last change on this file since 7856 was 7811, checked in by uli, 13 years ago

Rename all non-locales stuff from sirp to kofa.

  • Property svn:keywords set to Id
File size: 6.8 KB
Line 
1## $Id: converters.py 7811 2012-03-08 19:00:51Z 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"""Converters for zope.schema-based datatypes.
19"""
20import grok
21from zope.component import createObject
22from zope.formlib import form
23from zope.formlib.boolwidgets import CheckBoxWidget
24from zope.formlib.form import (
25    _widgetKey, WidgetInputError, ValidationError, InputErrors, expandPrefix)
26from zope.formlib.interfaces import IInputWidget, ISimpleInputWidget
27from zope.interface import Interface
28from zope.publisher.browser import TestRequest
29from waeup.kofa.interfaces import IObjectConverter
30
31class ExtendedCheckBoxWidget(CheckBoxWidget):
32    """A checkbox widget that supports more input values as True/False
33    markers.
34
35    The default bool widget expects the string 'on' as only valid
36    ``True`` value in HTML forms for bool fields.
37
38    This widget also accepts '1', 'true' and 'yes' for that. Also all
39    uppercase/lowecase combinations of these strings are accepted.
40
41    The widget still renders ``True`` to ``'on'`` when a form is
42    generated.
43    """
44    true_markers = ['1', 'true', 'on', 'yes']
45
46    def _toFieldValue(self, input):
47        """Convert from HTML presentation to Python bool."""
48        if not isinstance(input, basestring):
49            return False
50        return input.lower() in self.true_markers
51
52    def _getFormInput(self):
53        """Returns the form input used by `_toFieldValue`.
54
55        Return values:
56
57          ``'on'``  checkbox is checked
58          ``''``    checkbox is not checked
59          ``None``  form input was not provided
60
61        """
62        value = self.request.get(self.name)
63        if isinstance(value, basestring):
64            value = value.lower()
65        if value in self.true_markers:
66            return 'on'
67        elif self.name + '.used' in self.request:
68            return ''
69        else:
70            return None
71
72def getWidgetsData(widgets, form_prefix, data):
73    """Get data and validation errors from `widgets` for `data`.
74
75    Updates the dict in `data` with values from the widgets in
76    `widgets`.
77
78    Returns a list of tuples ``(<WIDGET_NAME>, <ERROR>)`` where
79    ``<WIDGET_NAME>`` is a widget name (normally the same as the
80    associated field name) and ``<ERROR>`` is the exception that
81    happened for that widget/field.
82
83    This is merely a copy from the same-named function in
84    :mod:`zope.formlib.form`. The only difference is that we also
85    store the fieldname for which a validation error happened in the
86    returned error list (what the original does not do).
87
88    """
89    errors = []
90    form_prefix = expandPrefix(form_prefix)
91
92    for input, widget in widgets.__iter_input_and_widget__():
93        if input and IInputWidget.providedBy(widget):
94            name = _widgetKey(widget, form_prefix)
95
96            if not widget.hasInput():
97                continue
98
99            try:
100                data[name] = widget.getInputValue()
101            except ValidationError, error:
102                # convert field ValidationError to WidgetInputError
103                error = WidgetInputError(widget.name, widget.label, error)
104                errors.append((name, error))
105            except InputErrors, error:
106                errors.append((name, error))
107
108    return errors
109
110
111class DefaultObjectConverter(grok.Adapter):
112    """Turn string values into real values.
113
114    A converter can convert string values for objects that implement a
115    certain interface into real values based on the given interface.
116    """
117
118    grok.context(Interface)
119    grok.provides(IObjectConverter)
120
121    def __init__(self, iface):
122        self.iface = iface
123        # Omit known dictionaries since there is no widget available
124        # for dictionary schema fields
125        self.default_form_fields = form.Fields(iface).omit('description_dict')
126        return
127
128    def fromStringDict(self, data_dict, context, form_fields=None):
129        """Convert values in `data_dict`.
130
131        Converts data in `data_dict` into real values based on
132        `context` and `form_fields`.
133
134        `data_dict` is a mapping (dict) from field names to values
135        represented as strings.
136
137        The fields (keys) to convert can be given in optional
138        `form_fields`. If given, form_fields should be an instance of
139        :class:`zope.formlib.form.Fields`. Suitable instances are for
140        example created by :class:`grok.AutoFields`.
141
142        If no `form_fields` are given, a default is computed from the
143        associated interface.
144
145        The `context` can be an existing object (implementing the
146        associated interface) or a factory name. If it is a string, we
147        try to create an object using
148        :func:`zope.component.createObject`.
149
150        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
151        <DATA_DICT>)`` where
152
153        ``<FIELD_ERRORS>``
154           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
155           error that happened when validating the input data in
156           `data_dict`
157
158        ``<INVARIANT_ERRORS>``
159           is a list of invariant errors concerning several fields
160
161        ``<DATA_DICT>``
162           is a dict with the values from input dict converted.
163
164        If errors happen, i.e. the error lists are not empty, always
165        an empty ``<DATA_DICT>`` is returned.
166
167        If ``<DATA_DICT>`` is non-empty, there were no errors.
168        """
169        if form_fields is None:
170            form_fields = self.default_form_fields
171
172        request = TestRequest(form={})
173        for key, val in data_dict.items():
174            request.form['form.%s' % key] = val
175
176        obj = context
177        if isinstance(context, basestring):
178            obj = createObject(context)
179
180        widgets = form.setUpInputWidgets(
181            form_fields, 'form', obj, request)
182
183        new_data = dict()
184        errors = getWidgetsData(widgets, 'form', new_data)
185
186        invariant_errors = form.checkInvariants(form_fields, new_data)
187        if errors or invariant_errors:
188            err_messages = [(key, err.args[0]) for key, err in errors]
189            invariant_errors = [err.message for err in invariant_errors]
190            return err_messages, invariant_errors, {}
191
192        return errors, invariant_errors, new_data
Note: See TracBrowser for help on using the repository browser.