# HG changeset patch # User Thierry Florac # Date 1540474404 -7200 # Node ID e46b0edbc87f1c8e9c73543c71d6f281dc9cde2f # Parent 0ab206c35370f3e9b0c05f3e3fa9b9f7f3207ae9 Use server's locale in Integer and Float input widgets diff -r 0ab206c35370 -r e46b0edbc87f src/pyams_form/interfaces/form.py --- a/src/pyams_form/interfaces/form.py Tue Oct 23 12:08:33 2018 +0200 +++ b/src/pyams_form/interfaces/form.py Thu Oct 25 15:33:24 2018 +0200 @@ -543,6 +543,18 @@ default=False) +class IIntegerWidget(ITextWidget): + """Integer widget interface""" + + +class IFloatWidget(ITextWidget): + """Float widget interface""" + + precision = Int(title=_("Widget display precision"), + description=_("Precision to use to display current value"), + default=2) + + class IDateWidget(ITextWidget): """Date widget interface""" diff -r 0ab206c35370 -r e46b0edbc87f src/pyams_form/widget/__init__.py --- a/src/pyams_form/widget/__init__.py Tue Oct 23 12:08:33 2018 +0200 +++ b/src/pyams_form/widget/__init__.py Thu Oct 25 15:33:24 2018 +0200 @@ -14,6 +14,7 @@ import inspect import json +import locale import os import venusian @@ -26,7 +27,8 @@ from z3c.form.browser.text import TextWidget from z3c.form.browser.textarea import TextAreaWidget from z3c.form.button import ButtonAction -from z3c.form.converter import BaseDataConverter, DatetimeDataConverter as BaseDatetimeDataConverter +from z3c.form.converter import BaseDataConverter, DatetimeDataConverter as BaseDatetimeDataConverter, \ + FormatterValidationError from z3c.form.interfaces import DISPLAY_MODE, IButtonAction, IDataConverter, IFieldWidget, INPUT_MODE, ITextWidget, \ IWidgetLayoutTemplate, NO_VALUE from z3c.form.widget import FieldWidget, WidgetLayoutFactory, WidgetTemplateFactory @@ -37,7 +39,8 @@ from zope.schema.interfaces import IChoice, IDate, IDatetime, IFloat, IInt, IList, ISet, ITime, ITuple from pyams_form.interfaces.form import IActionWidget, ICloseWidget, IColorWidget, IDateWidget, IDatetimeWidget, \ - IFormLayer, IHTMLWidget, IResetWidget, ISEOTextLineWidget, ISelect2Widget, ITextLineListWidget, ITimeWidget + IFormLayer, IHTMLWidget, IResetWidget, ISEOTextLineWidget, ISelect2Widget, ITextLineListWidget, ITimeWidget, \ + IIntegerWidget, IFloatWidget from pyams_form.schema import IActionButton, ICloseButton, IResetButton from pyams_skin.interfaces.tinymce import ITinyMCEConfiguration from pyams_utils.adapter import adapter_config @@ -303,8 +306,28 @@ # Integer field widget # +@adapter_config(context=(IInt, IIntegerWidget), provides=IDataConverter) +class IntegerFieldDataConverter(BaseDataConverter): + """Integer field data converter""" + + error_message = _("Invalid integer value") + + def toWidgetValue(self, value): + if value is self.field.missing_value: + return '' + return locale.format_string('%d', value, grouping=True) + + def toFieldValue(self, value): + if not value: + return self.field.missing_value + try: + return locale.atoi(value) + except ValueError: + raise FormatterValidationError(self.errorMessage, value) + + @widgettemplate_config(mode=INPUT_MODE, template='templates/integer-input.pt', layer=IFormLayer) -@implementer_only(ITextWidget) +@implementer_only(IIntegerWidget) class IntegerWidget(TextWidget): """Integer widget""" @@ -327,18 +350,39 @@ # Float field widget # +@adapter_config(context=(IFloat, IFloatWidget), provides=IDataConverter) +class FloatFieldDataConverter(BaseDataConverter): + """Float field data converter""" + + error_message = _("Invalid floating value") + + def toWidgetValue(self, value): + if value is self.field.missing_value: + return '' + return locale.format_string('%.{0}f'.format(self.widget.precision), value, grouping=True) + + def toFieldValue(self, value): + if not value: + return self.field.missing_value + try: + return locale.atof(value) + except ValueError: + raise FormatterValidationError(self.error_message, value) + + @widgettemplate_config(mode=INPUT_MODE, template='templates/float-input.pt', layer=IFormLayer) -@implementer_only(ITextWidget) +@implementer_only(IFloatWidget) class FloatWidget(TextWidget): """Float widget""" + precision = FieldProperty(IFloatWidget['precision']) + @property def validate_rules(self): - rules = {'number': True} - if self.field.min is not None: - rules['min'] = self.field.min - if self.field.max is not None: - rules['max'] = self.field.max + conv = locale.localeconv() + rules = {'pattern': '\{}?[\d{}]+{}?\d*'.format(conv.get('negative_sign', '-'), + conv.get('mon_thousands_sep', ''), + conv.get('decimal_point', '\.'))} return json.dumps(rules)