From 96a9cdbb1af5878813b7cde432ead5de065827f5 Mon Sep 17 00:00:00 2001 From: Garrett Smith Date: Wed, 9 Jun 2004 21:09:07 +0000 Subject: [PATCH] Merged r25318 from trunk. --- browser/textwidgets.py | 468 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 browser/textwidgets.py diff --git a/browser/textwidgets.py b/browser/textwidgets.py new file mode 100644 index 0000000..d1a4549 --- /dev/null +++ b/browser/textwidgets.py @@ -0,0 +1,468 @@ +############################################################################## +# +# Copyright (c) 2004 Zope Corporation and Contributors. +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# +############################################################################## +"""Browser widgets with text-based input + +$Id$ +""" +from zope.interface import implements + +from zope.app.form.interfaces import IInputWidget, ConversionError +from zope.app.form.browser.interfaces import ITextBrowserWidget +from zope.app.form.browser.widget import SimpleInputWidget, renderElement +from zope.app.datetimeutils import parseDatetimetz +from zope.app.datetimeutils import DateTimeError + + +class TextWidget(SimpleInputWidget): + """Text widget. + + Single-line text (unicode) input + + >>> from zope.publisher.browser import TestRequest + >>> from zope.schema import TextLine + >>> field = TextLine(__name__='foo', title=u'on') + >>> request = TestRequest(form={'field.foo': u'Bob'}) + >>> widget = TextWidget(field, request) + >>> widget.hasInput() + True + >>> widget.getInputValue() + u'Bob' + + >>> def normalize(s): + ... return '\\n '.join(filter(None, s.split(' '))) + + >>> print normalize( widget() ) + + + >>> print normalize( widget.hidden() ) + + + Calling setRenderedValue will change what gets output: + + >>> widget.setRenderedValue("Barry") + >>> print normalize( widget() ) + + + Check that HTML is correctly encoded and decoded: + + >>> request = TestRequest( + ... form={'field.foo': u'<h1>&copy;</h1>'}) + >>> widget = TextWidget(field, request) + >>> widget.getInputValue() + u'

©

' + + >>> print normalize( widget() ) + + """ + + implements(ITextBrowserWidget) + + default = '' + displayWidth = 20 + displayMaxWidth = "" + extra = '' + style = '' + convert_missing_value = True + + def __init__(self, *args): + super(TextWidget, self).__init__(*args) + + def __call__(self): + displayMaxWidth = self.displayMaxWidth or 0 + if displayMaxWidth > 0: + return renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + value=self._getFormValue(), + cssClass=self.cssClass, + style=self.style, + size=self.displayWidth, + maxlength=displayMaxWidth, + extra=self.extra) + else: + return renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + value=self._getFormValue(), + cssClass=self.cssClass, + style=self.style, + size=self.displayWidth, + extra=self.extra) + + def _toFieldValue(self, input): + if self.convert_missing_value and input == self._missing: + value = self.context.missing_value + else: + # XXX We convert everything to unicode :-( + # But I see no other way for to convert the + # value at the moment. + try: + value = unicode(input) + except ValueError, v: + raise ConversionError("Invalid integer data", v) + + return decode_html(value) + + +class Bytes(SimpleInputWidget): + + def _toFieldValue(self, input): + value = super(Bytes, self)._toFieldValue(input) + if type(value) is unicode: + try: + value = value.encode('ascii') + except UnicodeError, v: + raise ConversionError("Invalid textual data", v) + return value + + +class BytesWidget(Bytes, TextWidget): + """Bytes widget. + + Single-line data (string) input + + >>> from zope.publisher.browser import TestRequest + >>> from zope.schema import BytesLine + >>> field = BytesLine(__name__='foo', title=u'on') + >>> request = TestRequest(form={'field.foo': u'Bob'}) + >>> widget = BytesWidget(field, request) + >>> widget.hasInput() + True + >>> widget.getInputValue() + 'Bob' + """ + +class ASCII(Bytes): + """ASCII""" + + +class ASCIIWidget(BytesWidget): + """ASCII widget. + + Single-line data (string) input + """ + +class TextAreaWidget(SimpleInputWidget): + """TextArea widget. + + Multi-line text (unicode) input. + + >>> from zope.publisher.browser import TestRequest + >>> from zope.schema import Text + >>> field = Text(__name__='foo', title=u'on') + >>> request = TestRequest(form={'field.foo': u'Hello\\r\\nworld!'}) + >>> widget = TextAreaWidget(field, request) + >>> widget.hasInput() + True + >>> widget.getInputValue() + u'Hello\\nworld!' + + >>> def normalize(s): + ... return '\\n '.join(filter(None, s.split(' '))) + + >>> print normalize( widget() ) + + + >>> print normalize( widget.hidden() ) + + + Calling setRenderedValue will change what gets output: + + >>> widget.setRenderedValue("Hey\\ndude!") + >>> print normalize( widget() ) + + + Check that HTML is correctly encoded and decoded: + + >>> request = TestRequest( + ... form={'field.foo': u'<h1>&copy;</h1>'}) + >>> widget = TextAreaWidget(field, request) + >>> widget.getInputValue() + u'

©

' + + >>> print normalize( widget() ) + + """ + + default = "" + width = 60 + height = 15 + extra = "" + style = '' + + def _toFieldValue(self, value): + value = super(TextAreaWidget, self)._toFieldValue(value) + if value: + value = decode_html(value) + value = value.replace("\r\n", "\n") + # Converting the value to unicode. + try: + value = unicode(value) + except ValueError, v: + raise ConversionError("Invalid unicode data", v) + return value + + def _toFormValue(self, value): + value = super(TextAreaWidget, self)._toFormValue(value) + if value: + value = value.replace("\n", "\r\n") + value = encode_html(value) + return value + + def __call__(self): + return renderElement("textarea", + name=self.name, + id=self.name, + cssClass=self.cssClass, + rows=self.height, + cols=self.width, + style=self.style, + contents=self._getFormValue(), + extra=self.extra) + +class BytesAreaWidget(Bytes, TextAreaWidget): + """BytesArea widget. + + Multi-line string input. + + >>> from zope.publisher.browser import TestRequest + >>> from zope.schema import Bytes + >>> field = Bytes(__name__='foo', title=u'on') + >>> request = TestRequest(form={'field.foo': u'Hello\\r\\nworld!'}) + >>> widget = BytesAreaWidget(field, request) + >>> widget.hasInput() + True + >>> widget.getInputValue() + 'Hello\\nworld!' + """ + +class PasswordWidget(TextWidget): + """Password Widget""" + + type = 'password' + + def __call__(self): + displayMaxWidth = self.displayMaxWidth or 0 + if displayMaxWidth > 0: + return renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + value='', + cssClass=self.cssClass, + style=self.style, + size=self.displayWidth, + maxlength=displayMaxWidth, + extra=self.extra) + else: + return renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + value='', + cssClass=self.cssClass, + style=self.style, + size=self.displayWidth, + extra=self.extra) + + def hidden(self): + raise NotImplementedError( + 'Cannot get a hidden tag for a password field') + +class FileWidget(TextWidget): + """File Widget""" + + type = 'file' + + def __call__(self): + displayMaxWidth = self.displayMaxWidth or 0 + if displayMaxWidth > 0: + return renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + cssClass=self.cssClass, + size=self.displayWidth, + maxlength=displayMaxWidth, + extra=self.extra) + else: + return renderElement(self.tag, + type=self.type, + name=self.name, + id=self.name, + cssClass=self.cssClass, + size=self.displayWidth, + extra=self.extra) + + def hasInput(self): + file = self.request.form.get(self.name) + if file is None: + return False + + if getattr(file, 'filename', ''): + return True + + try: + seek = file.seek + read = file.read + except AttributeError: + return False + + seek(0) + if read(1): + return True + + return False + + def _toFieldValue(self, input): + try: + seek = input.seek + read = input.read + except AttributeError, e: + raise ConversionError('Form input is not a file object', e) + else: + seek(0) + data = read() + if data or getattr(input, 'filename', ''): + return data + else: + return self.context.missing_value + +class IntWidget(TextWidget): + displayWidth = 10 + + def _toFieldValue(self, input): + if input == self._missing: + return self.context.missing_value + else: + try: + return int(input) + except ValueError, v: + raise ConversionError("Invalid integer data", v) + + +class FloatWidget(TextWidget): + implements(IInputWidget) + displayWidth = 10 + + def _toFieldValue(self, input): + if input == self._missing: + return self.context.missing_value + else: + try: + return float(input) + except ValueError, v: + raise ConversionError("Invalid floating point data", v) + + +class DatetimeWidget(TextWidget): + """Datetime entry widget.""" + + displayWidth = 20 + + def _toFieldValue(self, input): + if input == self._missing: + return self.context.missing_value + else: + try: + return parseDatetimetz(input) + except (DateTimeError, ValueError, IndexError), v: + raise ConversionError("Invalid datetime data", v) + + +class DateWidget(TextWidget): + """Date entry widget. + """ + + displayWidth = 20 + + def _toFieldValue(self, input): + if input == self._missing: + return self.context.missing_value + else: + try: + return parseDatetimetz(input).date() + except (DateTimeError, ValueError, IndexError), v: + raise ConversionError("Invalid datetime data", v) + + +def encode_html(text): + if text: + text = text.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + text = text.replace('"', '"') + return text + + +def decode_html(text): + if text: + text = text.replace('&', '&') + text = text.replace('<', '<') + text = text.replace('>', '>') + text = text.replace('"', '"') + return text