123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- from __future__ import unicode_literals
- try:
- from html import escape
- except ImportError:
- from cgi import escape
- from wtforms.compat import text_type, iteritems
- __all__ = (
- 'CheckboxInput', 'FileInput', 'HiddenInput', 'ListWidget', 'PasswordInput',
- 'RadioInput', 'Select', 'SubmitInput', 'TableWidget', 'TextArea',
- 'TextInput', 'Option'
- )
- def html_params(**kwargs):
- """
- Generate HTML attribute syntax from inputted keyword arguments.
- The output value is sorted by the passed keys, to provide consistent output
- each time this function is called with the same parameters. Because of the
- frequent use of the normally reserved keywords `class` and `for`, suffixing
- these with an underscore will allow them to be used.
- In order to facilitate the use of ``data-`` attributes, the first underscore
- behind the ``data``-element is replaced with a hyphen.
- >>> html_params(data_any_attribute='something')
- 'data-any_attribute="something"'
- In addition, the values ``True`` and ``False`` are special:
- * ``attr=True`` generates the HTML compact output of a boolean attribute,
- e.g. ``checked=True`` will generate simply ``checked``
- * ``attr=False`` will be ignored and generate no output.
- >>> html_params(name='text1', id='f', class_='text')
- 'class="text" id="f" name="text1"'
- >>> html_params(checked=True, readonly=False, name="text1", abc="hello")
- 'abc="hello" checked name="text1"'
- """
- params = []
- for k, v in sorted(iteritems(kwargs)):
- if k in ('class_', 'class__', 'for_'):
- k = k[:-1]
- elif k.startswith('data_'):
- k = k.replace('_', '-', 1)
- if v is True:
- params.append(k)
- elif v is False:
- pass
- else:
- params.append('%s="%s"' % (text_type(k), escape(text_type(v), quote=True)))
- return ' '.join(params)
- class HTMLString(text_type):
- """
- This is an "HTML safe string" class that is returned by WTForms widgets.
- For the most part, HTMLString acts like a normal unicode string, except
- in that it has a `__html__` method. This method is invoked by a compatible
- auto-escaping HTML framework to get the HTML-safe version of a string.
- Usage::
- HTMLString('<input type="text" value="hello">')
- """
- def __html__(self):
- """
- Give an HTML-safe string.
- This method actually returns itself, because it's assumed that
- whatever you give to HTMLString is a string with any unsafe values
- already escaped. This lets auto-escaping template frameworks
- know that this string is safe for HTML rendering.
- """
- return self
- class ListWidget(object):
- """
- Renders a list of fields as a `ul` or `ol` list.
- This is used for fields which encapsulate many inner fields as subfields.
- The widget will try to iterate the field to get access to the subfields and
- call them to render them.
- If `prefix_label` is set, the subfield's label is printed before the field,
- otherwise afterwards. The latter is useful for iterating radios or
- checkboxes.
- """
- def __init__(self, html_tag='ul', prefix_label=True):
- assert html_tag in ('ol', 'ul')
- self.html_tag = html_tag
- self.prefix_label = prefix_label
- def __call__(self, field, **kwargs):
- kwargs.setdefault('id', field.id)
- html = ['<%s %s>' % (self.html_tag, html_params(**kwargs))]
- for subfield in field:
- if self.prefix_label:
- html.append('<li>%s %s</li>' % (subfield.label, subfield()))
- else:
- html.append('<li>%s %s</li>' % (subfield(), subfield.label))
- html.append('</%s>' % self.html_tag)
- return HTMLString(''.join(html))
- class TableWidget(object):
- """
- Renders a list of fields as a set of table rows with th/td pairs.
- If `with_table_tag` is True, then an enclosing <table> is placed around the
- rows.
- Hidden fields will not be displayed with a row, instead the field will be
- pushed into a subsequent table row to ensure XHTML validity. Hidden fields
- at the end of the field list will appear outside the table.
- """
- def __init__(self, with_table_tag=True):
- self.with_table_tag = with_table_tag
- def __call__(self, field, **kwargs):
- html = []
- if self.with_table_tag:
- kwargs.setdefault('id', field.id)
- html.append('<table %s>' % html_params(**kwargs))
- hidden = ''
- for subfield in field:
- if subfield.type in ('HiddenField', 'CSRFTokenField'):
- hidden += text_type(subfield)
- else:
- html.append('<tr><th>%s</th><td>%s%s</td></tr>' % (text_type(subfield.label), hidden, text_type(subfield)))
- hidden = ''
- if self.with_table_tag:
- html.append('</table>')
- if hidden:
- html.append(hidden)
- return HTMLString(''.join(html))
- class Input(object):
- """
- Render a basic ``<input>`` field.
- This is used as the basis for most of the other input fields.
- By default, the `_value()` method will be called upon the associated field
- to provide the ``value=`` HTML attribute.
- """
- html_params = staticmethod(html_params)
- def __init__(self, input_type=None):
- if input_type is not None:
- self.input_type = input_type
- def __call__(self, field, **kwargs):
- kwargs.setdefault('id', field.id)
- kwargs.setdefault('type', self.input_type)
- if 'value' not in kwargs:
- kwargs['value'] = field._value()
- return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))
- class TextInput(Input):
- """
- Render a single-line text input.
- """
- input_type = 'text'
- class PasswordInput(Input):
- """
- Render a password input.
- For security purposes, this field will not reproduce the value on a form
- submit by default. To have the value filled in, set `hide_value` to
- `False`.
- """
- input_type = 'password'
- def __init__(self, hide_value=True):
- self.hide_value = hide_value
- def __call__(self, field, **kwargs):
- if self.hide_value:
- kwargs['value'] = ''
- return super(PasswordInput, self).__call__(field, **kwargs)
- class HiddenInput(Input):
- """
- Render a hidden input.
- """
- field_flags = ('hidden', )
- input_type = 'hidden'
- class CheckboxInput(Input):
- """
- Render a checkbox.
- The ``checked`` HTML attribute is set if the field's data is a non-false value.
- """
- input_type = 'checkbox'
- def __call__(self, field, **kwargs):
- if getattr(field, 'checked', field.data):
- kwargs['checked'] = True
- return super(CheckboxInput, self).__call__(field, **kwargs)
- class RadioInput(Input):
- """
- Render a single radio button.
- This widget is most commonly used in conjunction with ListWidget or some
- other listing, as singular radio buttons are not very useful.
- """
- input_type = 'radio'
- def __call__(self, field, **kwargs):
- if field.checked:
- kwargs['checked'] = True
- return super(RadioInput, self).__call__(field, **kwargs)
- class FileInput(object):
- """
- Renders a file input chooser field.
- """
- def __call__(self, field, **kwargs):
- kwargs.setdefault('id', field.id)
- return HTMLString('<input %s>' % html_params(name=field.name, type='file', **kwargs))
- class SubmitInput(Input):
- """
- Renders a submit button.
- The field's label is used as the text of the submit button instead of the
- data on the field.
- """
- input_type = 'submit'
- def __call__(self, field, **kwargs):
- kwargs.setdefault('value', field.label.text)
- return super(SubmitInput, self).__call__(field, **kwargs)
- class TextArea(object):
- """
- Renders a multi-line text area.
- `rows` and `cols` ought to be passed as keyword args when rendering.
- """
- def __call__(self, field, **kwargs):
- kwargs.setdefault('id', field.id)
- return HTMLString('<textarea %s>%s</textarea>' % (
- html_params(name=field.name, **kwargs),
- escape(text_type(field._value()), quote=False)
- ))
- class Select(object):
- """
- Renders a select field.
- If `multiple` is True, then the `size` property should be specified on
- rendering to make the field useful.
- The field must provide an `iter_choices()` method which the widget will
- call on rendering; this method must yield tuples of
- `(value, label, selected)`.
- """
- def __init__(self, multiple=False):
- self.multiple = multiple
- def __call__(self, field, **kwargs):
- kwargs.setdefault('id', field.id)
- if self.multiple:
- kwargs['multiple'] = True
- html = ['<select %s>' % html_params(name=field.name, **kwargs)]
- for val, label, selected in field.iter_choices():
- html.append(self.render_option(val, label, selected))
- html.append('</select>')
- return HTMLString(''.join(html))
- @classmethod
- def render_option(cls, value, label, selected, **kwargs):
- if value is True:
- # Handle the special case of a 'True' value.
- value = text_type(value)
- options = dict(kwargs, value=value)
- if selected:
- options['selected'] = True
- return HTMLString('<option %s>%s</option>' % (html_params(**options), escape(text_type(label), quote=False)))
- class Option(object):
- """
- Renders the individual option from a select field.
- This is just a convenience for various custom rendering situations, and an
- option by itself does not constitute an entire field.
- """
- def __call__(self, field, **kwargs):
- return Select.render_option(field._value(), field.label.text, field.checked, **kwargs)
|