changeset 275:d91cbdeb75e9 trunk

Integrated `HTMLFormFiller` filter initially presented as a [wiki:FormFilling#Usingatemplatefilter recipe].
author cmlenz
date Sun, 01 Oct 2006 22:54:57 +0000
parents f2b8932a610e
children 8c4ed33069c6
files genshi/filters.py genshi/tests/filters.py
diffstat 2 files changed, 398 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- 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('''<form>
+    ...   <p><input type="text" name="foo" /></p>
+    ... </form>''')
+    >>> filler = HTMLFormFiller(data={'foo': 'bar'})
+    >>> print html | filler
+    <form>
+      <p><input type="text" name="foo" value="bar"/></p>
+    </form>
+    """
+    # 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):
--- 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("""<form><p>
+          <input type="text" name="foo" />
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <input type="text" name="foo"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_text_single_value(self):
+        html = HTML("""<form><p>
+          <input type="text" name="foo" />
+        </p></form>""") | HTMLFormFiller(data={'foo': 'bar'})
+        self.assertEquals("""<form><p>
+          <input type="text" name="foo" value="bar"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_text_multi_value(self):
+        html = HTML("""<form><p>
+          <input type="text" name="foo" />
+        </p></form>""") | HTMLFormFiller(data={'foo': ['bar']})
+        self.assertEquals("""<form><p>
+          <input type="text" name="foo" value="bar"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_hidden_no_value(self):
+        html = HTML("""<form><p>
+          <input type="hidden" name="foo" />
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <input type="hidden" name="foo"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_hidden_single_value(self):
+        html = HTML("""<form><p>
+          <input type="hidden" name="foo" />
+        </p></form>""") | HTMLFormFiller(data={'foo': 'bar'})
+        self.assertEquals("""<form><p>
+          <input type="hidden" name="foo" value="bar"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_hidden_multi_value(self):
+        html = HTML("""<form><p>
+          <input type="hidden" name="foo" />
+        </p></form>""") | HTMLFormFiller(data={'foo': ['bar']})
+        self.assertEquals("""<form><p>
+          <input type="hidden" name="foo" value="bar"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_textarea_no_value(self):
+        html = HTML("""<form><p>
+          <textarea name="foo"></textarea>
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <textarea name="foo"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_textarea_single_value(self):
+        html = HTML("""<form><p>
+          <textarea name="foo"></textarea>
+        </p></form>""") | HTMLFormFiller(data={'foo': 'bar'})
+        self.assertEquals("""<form><p>
+          <textarea name="foo">bar</textarea>
+        </p></form>""", unicode(html))
+
+    def test_fill_textarea_multi_value(self):
+        html = HTML("""<form><p>
+          <textarea name="foo"></textarea>
+        </p></form>""") | HTMLFormFiller(data={'foo': ['bar']})
+        self.assertEquals("""<form><p>
+          <textarea name="foo">bar</textarea>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_checkbox_no_value(self):
+        html = HTML("""<form><p>
+          <input type="checkbox" name="foo" />
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_checkbox_single_value_auto(self):
+        html = HTML("""<form><p>
+          <input type="checkbox" name="foo" />
+        </p></form>""")
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': ''})))
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo" checked="checked"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': 'on'})))
+
+    def test_fill_input_checkbox_single_value_defined(self):
+        html = HTML("""<form><p>
+          <input type="checkbox" name="foo" value="1" />
+        </p></form>""")
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo" value="1" checked="checked"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': '1'})))
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo" value="1"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': '2'})))
+
+    def test_fill_input_checkbox_multi_value_auto(self):
+        html = HTML("""<form><p>
+          <input type="checkbox" name="foo" />
+        </p></form>""")
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': []})))
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo" checked="checked"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': ['on']})))
+
+    def test_fill_input_checkbox_multi_value_defined(self):
+        html = HTML("""<form><p>
+          <input type="checkbox" name="foo" value="1" />
+        </p></form>""")
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo" value="1" checked="checked"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': ['1']})))
+        self.assertEquals("""<form><p>
+          <input type="checkbox" name="foo" value="1"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': ['2']})))
+
+    def test_fill_input_radio_no_value(self):
+        html = HTML("""<form><p>
+          <input type="radio" name="foo" />
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <input type="radio" name="foo"/>
+        </p></form>""", unicode(html))
+
+    def test_fill_input_radio_single_value(self):
+        html = HTML("""<form><p>
+          <input type="radio" name="foo" value="1" />
+        </p></form>""")
+        self.assertEquals("""<form><p>
+          <input type="radio" name="foo" value="1" checked="checked"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': '1'})))
+        self.assertEquals("""<form><p>
+          <input type="radio" name="foo" value="1"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': '2'})))
+
+    def test_fill_input_radio_multi_value(self):
+        html = HTML("""<form><p>
+          <input type="radio" name="foo" value="1" />
+        </p></form>""")
+        self.assertEquals("""<form><p>
+          <input type="radio" name="foo" value="1" checked="checked"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': ['1']})))
+        self.assertEquals("""<form><p>
+          <input type="radio" name="foo" value="1"/>
+        </p></form>""", unicode(html | HTMLFormFiller(data={'foo': ['2']})))
+
+    def test_fill_select_no_value_auto(self):
+        html = HTML("""<form><p>
+          <select name="foo">
+            <option>1</option>
+            <option>2</option>
+            <option>3</option>
+          </select>
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <select name="foo">
+            <option>1</option>
+            <option>2</option>
+            <option>3</option>
+          </select>
+        </p></form>""", unicode(html))
+
+    def test_fill_select_no_value_defined(self):
+        html = HTML("""<form><p>
+          <select name="foo">
+            <option value="1">1</option>
+            <option value="2">2</option>
+            <option value="3">3</option>
+          </select>
+        </p></form>""") | HTMLFormFiller()
+        self.assertEquals("""<form><p>
+          <select name="foo">
+            <option value="1">1</option>
+            <option value="2">2</option>
+            <option value="3">3</option>
+          </select>
+        </p></form>""", unicode(html))
+
+    def test_fill_select_single_value_auto(self):
+        html = HTML("""<form><p>
+          <select name="foo">
+            <option>1</option>
+            <option>2</option>
+            <option>3</option>
+          </select>
+        </p></form>""") | HTMLFormFiller(data={'foo': '1'})
+        self.assertEquals("""<form><p>
+          <select name="foo">
+            <option selected="selected">1</option>
+            <option>2</option>
+            <option>3</option>
+          </select>
+        </p></form>""", unicode(html))
+
+    def test_fill_select_single_value_defined(self):
+        html = HTML("""<form><p>
+          <select name="foo">
+            <option value="1">1</option>
+            <option value="2">2</option>
+            <option value="3">3</option>
+          </select>
+        </p></form>""") | HTMLFormFiller(data={'foo': '1'})
+        self.assertEquals("""<form><p>
+          <select name="foo">
+            <option value="1" selected="selected">1</option>
+            <option value="2">2</option>
+            <option value="3">3</option>
+          </select>
+        </p></form>""", unicode(html))
+
+    def test_fill_select_multi_value_auto(self):
+        html = HTML("""<form><p>
+          <select name="foo" multiple>
+            <option>1</option>
+            <option>2</option>
+            <option>3</option>
+          </select>
+        </p></form>""") | HTMLFormFiller(data={'foo': ['1', '3']})
+        self.assertEquals("""<form><p>
+          <select name="foo" multiple="multiple">
+            <option selected="selected">1</option>
+            <option>2</option>
+            <option selected="selected">3</option>
+          </select>
+        </p></form>""", unicode(html))
+
+    def test_fill_select_multi_value_defined(self):
+        html = HTML("""<form><p>
+          <select name="foo" multiple>
+            <option value="1">1</option>
+            <option value="2">2</option>
+            <option value="3">3</option>
+          </select>
+        </p></form>""") | HTMLFormFiller(data={'foo': ['1', '3']})
+        self.assertEquals("""<form><p>
+          <select name="foo" multiple="multiple">
+            <option value="1" selected="selected">1</option>
+            <option value="2">2</option>
+            <option value="3" selected="selected">3</option>
+          </select>
+        </p></form>""", 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
Copyright (C) 2012-2017 Edgewall Software