# HG changeset patch # User cmlenz # Date 1159743297 0 # Node ID d91cbdeb75e9ee612c7c1062e537712a2dd91d99 # Parent f2b8932a610e1a15030eb2cc80dd921f636fa9b9 Integrated `HTMLFormFiller` filter initially presented as a [wiki:FormFilling#Usingatemplatefilter recipe]. diff --git a/genshi/filters.py b/genshi/filters.py --- a/genshi/filters.py +++ b/genshi/filters.py @@ -20,9 +20,148 @@ import re from genshi.core import Attrs, Namespace, stripentities -from genshi.core import END, END_NS, START, START_NS +from genshi.core import END, END_NS, START, START_NS, TEXT -__all__ = ['HTMLSanitizer', 'IncludeFilter'] +__all__ = ['HTMLFormFiller', 'HTMLSanitizer', 'IncludeFilter'] + + +class HTMLFormFiller(object): + """A stream filter that can populate HTML forms from a dictionary of values. + + >>> from genshi.input import HTML + >>> html = HTML('''
+ ...

+ ...
''') + >>> filler = HTMLFormFiller(data={'foo': 'bar'}) + >>> print html | filler +
+

+
+ """ + # TODO: only select the first radio button, and the first select option + # (if not in a multiple-select) + # TODO: only apply to elements in the XHTML namespace (or no namespace)? + + def __init__(self, name=None, id=None, data=None): + """Create the filter. + + @param name: The name of the form that should be populated. If this + parameter is given, only forms where the ``name`` attribute value + matches the parameter are processed. + @param id: The ID of the form that should be populated. If this + parameter is given, only forms where the ``id`` attribute value + matches the parameter are processed. + @param data: The dictionary of form values, where the keys are the names + of the form fields, and the values are the values to fill in. + """ + self.name = name + self.id = id + if data is None: + data = {} + self.data = data + + def __call__(self, stream, ctxt=None): + """Apply the filter to the given stream.""" + in_form = in_select = in_option = in_textarea = False + select_value = option_value = textarea_value = None + option_start = option_text = None + + for kind, data, pos in stream: + + if kind is START: + tag, attrib = data + tagname = tag.localname + + if tagname == 'form' and ( + self.name and attrib.get('name') == self.name or + self.id and attrib.get('id') == self.id or + not (self.id or self.name)): + in_form = True + + elif in_form: + if tagname == 'input': + type = attrib.get('type') + if type in ('checkbox', 'radio'): + name = attrib.get('name') + if name: + value = self.data.get(name) + declval = attrib.get('value') + checked = False + if isinstance(value, (list, tuple)): + if declval: + checked = declval in value + else: + checked = bool(filter(None, value)) + else: + if declval: + checked = declval == value + elif type == 'checkbox': + checked = bool(value) + if checked: + attrib.set('checked', 'checked') + else: + attrib.remove('checked') + elif type in (None, 'hidden', 'text'): + name = attrib.get('name') + if name: + value = self.data.get(name) + if isinstance(value, (list, tuple)): + value = value[0] + if value is not None: + attrib.set('value', unicode(value)) + elif tagname == 'select': + name = attrib.get('name') + select_value = self.data.get(name) + in_select = True + elif tagname == 'textarea': + name = attrib.get('name') + textarea_value = self.data.get(name) + if isinstance(textarea_value, (list, tuple)): + textarea_value = textarea_value[0] + in_textarea = True + elif in_select and tagname == 'option': + option_start = kind, data, pos + option_value = attrib.get('value') + in_option = True + continue + + elif in_form and kind is TEXT: + if in_select and in_option: + if option_value is None: + option_value = data + option_text = kind, data, pos + continue + elif in_textarea: + continue + + elif in_form and kind is END: + tagname = data.localname + if tagname == 'form': + in_form = False + elif tagname == 'select': + in_select = False + select_value = None + elif in_select and tagname == 'option': + if isinstance(select_value, (tuple, list)): + selected = option_value in select_value + else: + selected = option_value == select_value + attrib = option_start[1][1] + if selected: + attrib.set('selected', 'selected') + else: + attrib.remove('selected') + yield option_start + if option_text: + yield option_text + in_option = False + option_start = option_text = option_value = None + elif tagname == 'textarea': + if textarea_value: + yield TEXT, unicode(textarea_value), pos + in_textarea = False + + yield kind, data, pos class HTMLSanitizer(object): diff --git a/genshi/tests/filters.py b/genshi/tests/filters.py --- a/genshi/tests/filters.py +++ b/genshi/tests/filters.py @@ -17,12 +17,266 @@ import tempfile import unittest +from genshi import filters from genshi.core import Stream from genshi.input import HTML, ParseError -from genshi.filters import HTMLSanitizer +from genshi.filters import HTMLFormFiller, HTMLSanitizer from genshi.template import TemplateLoader +class HTMLFormFillerTestCase(unittest.TestCase): + + def test_fill_input_text_no_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_text_single_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': 'bar'}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_text_multi_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['bar']}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_hidden_no_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_hidden_single_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': 'bar'}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_hidden_multi_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['bar']}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_textarea_no_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""") | HTMLFormFiller(data={'foo': 'bar'}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_textarea_multi_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['bar']}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_checkbox_no_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_checkbox_single_value_auto(self): + html = HTML("""

+ +

""") + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': ''}))) + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': 'on'}))) + + def test_fill_input_checkbox_single_value_defined(self): + html = HTML("""

+ +

""") + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': '1'}))) + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': '2'}))) + + def test_fill_input_checkbox_multi_value_auto(self): + html = HTML("""

+ +

""") + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': []}))) + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': ['on']}))) + + def test_fill_input_checkbox_multi_value_defined(self): + html = HTML("""

+ +

""") + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': ['1']}))) + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': ['2']}))) + + def test_fill_input_radio_no_value(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_input_radio_single_value(self): + html = HTML("""

+ +

""") + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': '1'}))) + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': '2'}))) + + def test_fill_input_radio_multi_value(self): + html = HTML("""

+ +

""") + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': ['1']}))) + self.assertEquals("""

+ +

""", unicode(html | HTMLFormFiller(data={'foo': ['2']}))) + + def test_fill_select_no_value_auto(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_select_no_value_defined(self): + html = HTML("""

+ +

""") | HTMLFormFiller() + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_select_single_value_auto(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': '1'}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_select_single_value_defined(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': '1'}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_select_multi_value_auto(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['1', '3']}) + self.assertEquals("""

+ +

""", unicode(html)) + + def test_fill_select_multi_value_defined(self): + html = HTML("""

+ +

""") | HTMLFormFiller(data={'foo': ['1', '3']}) + self.assertEquals("""

+ +

""", unicode(html)) + + class HTMLSanitizerTestCase(unittest.TestCase): def test_sanitize_unchanged(self): @@ -156,6 +410,8 @@ def suite(): suite = unittest.TestSuite() + suite.addTest(doctest.DocTestSuite(filters)) + suite.addTest(unittest.makeSuite(HTMLFormFillerTestCase, 'test')) suite.addTest(unittest.makeSuite(HTMLSanitizerTestCase, 'test')) suite.addTest(unittest.makeSuite(IncludeFilterTestCase, 'test')) return suite