source: main/waeup.sirp/branches/henrik-bootstrap/src/waeup/sirp/utils/converters.py @ 11105

Last change on this file since 11105 was 7196, checked in by Henrik Bettermann, 13 years ago

More copyright adjustments (job finished).

  • Property svn:keywords set to Id
File size: 6.6 KB
Line 
1## $Id: converters.py 7196 2011-11-25 07:44:52Z henrik $
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.sirp.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        self.default_form_fields = form.Fields(iface)
124        return
125
126    def fromStringDict(self, data_dict, context, form_fields=None):
127        """Convert values in `data_dict`.
128
129        Converts data in `data_dict` into real values based on
130        `context` and `form_fields`.
131
132        `data_dict` is a mapping (dict) from field names to values
133        represented as strings.
134
135        The fields (keys) to convert can be given in optional
136        `form_fields`. If given, form_fields should be an instance of
137        :class:`zope.formlib.form.Fields`. Suitable instances are for
138        example created by :class:`grok.AutoFields`.
139
140        If no `form_fields` are given, a default is computed from the
141        associated interface.
142
143        The `context` can be an existing object (implementing the
144        associated interface) or a factory name. If it is a string, we
145        try to create an object using
146        :func:`zope.component.createObject`.
147
148        Returns a tuple ``(<FIELD_ERRORS>, <INVARIANT_ERRORS>,
149        <DATA_DICT>)`` where
150
151        ``<FIELD_ERRORS>``
152           is a list of tuples ``(<FIELD_NAME>, <ERROR>)`` for each
153           error that happened when validating the input data in
154           `data_dict`
155
156        ``<INVARIANT_ERRORS>``
157           is a list of invariant errors concerning several fields
158
159        ``<DATA_DICT>``
160           is a dict with the values from input dict converted.
161
162        If errors happen, i.e. the error lists are not empty, always
163        an empty ``<DATA_DICT>`` is returned.
164
165        If ``<DATA_DICT>` is non-empty, there were no errors.
166        """
167        if form_fields is None:
168            form_fields = self.default_form_fields
169
170        request = TestRequest(form={})
171        for key, val in data_dict.items():
172            request.form['form.%s' % key] = val
173
174        obj = context
175        if isinstance(context, basestring):
176            obj = createObject(context)
177
178        widgets = form.setUpInputWidgets(
179            form_fields, 'form', obj, request)
180
181        new_data = dict()
182        errors = getWidgetsData(widgets, 'form', new_data)
183
184        invariant_errors = form.checkInvariants(form_fields, new_data)
185        if errors or invariant_errors:
186            err_messages = [(key, err.args[0]) for key, err in errors]
187            invariant_errors = [err.message for err in invariant_errors]
188            return err_messages, invariant_errors, {}
189
190        return errors, invariant_errors, new_data
Note: See TracBrowser for help on using the repository browser.