source: main/waeup.kofa/trunk/src/waeup/kofa/widgets/datewidget.py @ 8171

Last change on this file since 8171 was 8152, checked in by uli, 13 years ago
  • Add new default date widgets for different date formats.
  • In FormattedDateWidgets? use own default also, if no date format was set with the connected schema field. In former implementation we expected always a valid date_format from connected schema field or no date_format at all. But as FormattedDate? schema fields now may also return None as date_format, which means that the date_format was not set at all in the interface, we can override this value with respective default values.

As a result, when setting no date_format in an interface with
FormattedDate? schema fields and when rendered with one of the
FormattedDateWidgets?, the default format of the respective widget
will be used to render the value: %d.%m.%Y for german dates, etc.

  • Property svn:keywords set to Id
File size: 8.2 KB
RevLine 
[7196]1## $Id: datewidget.py 8152 2012-04-14 14:13:39Z uli $
[6050]2##
[6867]3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
[6050]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.
[7196]8##
[6050]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.
[7196]13##
[6050]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"""
19A datewidget with customizable date format.
20"""
21from datetime import datetime
[6056]22from zope.formlib.interfaces import ConversionError, IDisplayWidget
[8147]23from zope.formlib.textwidgets import DateWidget, DateDisplayWidget, escape
[6056]24from zope.formlib.widget import renderElement, CustomWidgetFactory
25from zope.interface import implements
[6050]26
[6056]27#: A dictionary of supported date formats.
[6057]28#:
29#: The following formats are supported:
30#:
31#: ``iso``
32#:    ISO format ``YYYY-MM-DD``
33#: ``le``
34#:    little endian with slashes: ``DD/MM/YYYY``
35#: ``de``
36#:    german date format: ``DD.MM.YYYY``
37#: ``us``
38#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
39#:
40#: Furthermore we support for input widgets an additional year
41#: marker. Input date widgets with this marker provide also a year
42#: selector, handy for dates of birth etc.
43#:
44#: The year-supporting formats are similar to the basic versions above:
45#:
46#: ``iso-year``
47#:    ISO format ``YYYY-MM-DD``
48#: ``le-year``
49#:    little endian with slashes: ``DD/MM/YYYY``
50#: ``de-year``
51#:    german date format: ``DD.MM.YYYY``
52#: ``us-year``
53#:    middle endian format common in the U.S.: ``MM/DD/YYYY``
[8147]54#:
[6057]55#: For date display widgets there is naturally no difference between a
56#: year and non-year setting (you can for instance use 'le' or 'le-year'
57#: with the same output).
[6056]58DATE_FORMATS = {
[6060]59    'iso': ('datepicker', '%Y-%m-%d'),
60    'le':  ('datepicker-le', '%d/%m/%Y'),
61    'de':  ('datepicker-de', '%d.%m.%Y'),
62    'us':  ('datepicker-us', '%m/%d/%Y'),
63    'iso-year': ('datepicker-year', '%Y-%m-%d'),
64    'le-year':  ('datepicker-le-year', '%d/%m/%Y'),
65    'de-year':  ('datepicker-de-year', '%d.%m.%Y'),
66    'us-year':  ('datepicker-us-year', '%m/%d/%Y'),
[6056]67    }
[8147]68
69#: a dict containing tuples (<FORMAT>, <SHOW_YEAR>) as keys and
70#: suitable CSS tags as values.
71FORMATS_BY_VALUE = dict(
72    [((val[1], 'year' in key), val[0]) for key, val in DATE_FORMATS.items()])
73
74class FormattedDateWidget(DateWidget):
75    """A date widget that supports different (and _explicit_) date formats.
76
77    If the widget is bound to a schema field with respective
78    attributes, it reads its `show_year` and `date_format` attributes
79    (see waeup.kofa.schema.FormattedDate for an example) and sets a
80    CSS tag according to these values.
81
82    The widget also accepts ISO format as a fallback, even if a
83    different format was set. This should help with imports.
84
85    This is an input widget.
86    """
87    date_format = '%Y-%m-%d'
88    show_year = False
89
90    def __init__(self, context, request, *args, **kw):
91        # try to grab date_format and show_year from bound schema field.
[8152]92        date_format = getattr(context, 'date_format', self.date_format)
93        if date_format is not None:
94            self.date_format = date_format
[8147]95        self.show_year = getattr(context, 'show_year', self.show_year)
[8150]96        # add css class determined by date_format and show_year
97        css_cls = FORMATS_BY_VALUE.get((self.date_format, self.show_year), '')
98        self.cssClass = ' '.join([self.cssClass, css_cls]).strip()
[8147]99        return super(FormattedDateWidget, self).__init__(
100            context, request, *args, **kw)
101
102    def _toFieldValue(self, input):
103        # In import files we can use the hash symbol at the end of a
104        # date string to avoid annoying automatic date transformation
105        # by Excel or Calc
106        input = input.strip('#')
107        if input == self._missing:
108            return self.context.missing_value
109        else:
110            try:
111                value = datetime.strptime(input, self.date_format)
112            except (ValueError, IndexError), v:
113                try:
114                    # Try ISO format as fallback.
115                    # This is needed for instance during imports.
116                    value = datetime.strptime(
117                        input, FormattedDateWidget.date_format)
118                except (ValueError, IndexError), v:
119                    raise ConversionError("Invalid datetime data", v)
120        return value.date()
121
122    def _toFormValue(self, value):
123        if value:
124            value = value.strftime(self.date_format)
125        return value
126
[8150]127
[8147]128class FormattedDateDisplayWidget(DateDisplayWidget):
129    """A date widget that supports different (and _explicit_) date formats.
130
131    This is a display widget.
132    """
133    date_format = '%Y-%m-%d'
[8150]134    show_year = False
135
[8147]136    implements(IDisplayWidget)
137
[8150]138    def __init__(self, context, request, *args, **kw):
139        # try to grab date_format and show_year from bound schema field.
[8152]140        date_format = getattr(context, 'date_format', self.date_format)
141        if date_format is not None:
142            self.date_format = date_format
[8150]143        self.show_year = getattr(context, 'show_year', self.show_year)
144        return super(FormattedDateDisplayWidget, self).__init__(
145            context, request, *args, **kw)
146
[8147]147    def __call__(self):
148        if self._renderedValueSet():
149            content = self._data
150        else:
151            content = self.context.default
152        if content == self.context.missing_value:
153            return ""
154        content = content.strftime(self.date_format)
155        return renderElement("span", contents=escape(content),
156                             cssClass=self.cssClass)
157
[8152]158class DateLEWidget(FormattedDateWidget):
159    date_format = '%d/%m/%Y'
160
161class DateDEWidget(FormattedDateWidget):
162    date_format = '%d.%m.%Y'
163
164class DateUSWidget(FormattedDateWidget):
165    date_format = '%m/%d/%Y'
166
167class DateLEDisplayWidget(FormattedDateDisplayWidget):
168    date_format = '%d/%m/%Y'
169
170class DateDEDisplayWidget(FormattedDateDisplayWidget):
171    date_format = '%d.%m.%Y'
172
173class DateUSDisplayWidget(FormattedDateDisplayWidget):
174    date_format = '%m/%d/%Y'
175
[6056]176def FriendlyDateWidget(format):
177    """Get a friendly date input widget for `format`.
[6057]178
179    This widget is suitable for edit and add forms.
[8147]180
[7599]181    Valid `format` values are the keys of `DATE_FORMATS`
[6057]182    dict. Default is ``le`` (little endian; DD/MM/YYYY).
183
184    Friendly date widgets are rendered with a specialized CSS tag for
185    enabling JavaScript datepickers.
[6056]186    """
187    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
188    return CustomWidgetFactory(
189        FormattedDateWidget,
190        cssClass=css_class,
191        date_format=date_format)
192
193def FriendlyDateDisplayWidget(format):
194    """Get a friendly date display widget for `format`.
[6057]195
196    This widget is suitable for display forms.
[8147]197
[7599]198    Valid `format` values are the keys of `DATE_FORMATS`
[6057]199    dict. Default is ``le`` (little endian; DD/MM/YYYY).
200
[6060]201    This widget is not rendered with a specialized CSS tag for
[8147]202    enabling JavaScript datepickers. `css_class` is ignored which means
[6060]203    there is nor difference between e.g. ``le`` and ``le-year``.`
[6056]204    """
205    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
206    return CustomWidgetFactory(
207        FormattedDateDisplayWidget,
208        date_format=date_format)
[6867]209
210def FriendlyDatetimeDisplayWidget(format):
211    """Get a friendly datetime display widget for `format`.
212
213    This widget is suitable for display forms.
214
[7599]215    Valid `format` values are the keys of `DATE_FORMATS`
[6868]216    dict. Default is ``le`` (little endian; DD/MM/YYYY %H:%M:%S).
[6867]217
218    This widget is not rendered with a specialized CSS tag for
219    enabling JavaScript datepickers. `css_class` is ignored which means
[8147]220    there is no difference between e.g. ``le`` and ``le-year``.`
[6867]221    """
222    css_class, date_format = DATE_FORMATS.get(format, DATE_FORMATS['le'])
223    datetime_format = date_format + ' %H:%M:%S'
224    return CustomWidgetFactory(
[6868]225        FormattedDateDisplayWidget,
226        date_format=datetime_format)
Note: See TracBrowser for help on using the repository browser.