Source code for chrysalio.lib.form

"""Form validation and rendering library."""
# pylint: disable = too-many-lines

from __future__ import annotations
from re import sub as re_sub

from webob.compat import cgi_FieldStorage as FieldStorage
import colander
from pyramid.csrf import get_csrf_token, new_csrf_token
from pyramid.request import Request

from ..helpers.literal import Literal
from ..helpers.builder import Builder
from ..helpers import tags
from .i18n import _


# =============================================================================
[docs] def get_action(request: Request, silent: bool = False) -> tuple[str, tuple]: """Return a tuple such as ``(action, targets)`` where ``action`` is a string such as ``<act><?|!><target_id>`` and ``targets`` is a list of selected targets in a list form. :type request: pyramid.request.Request :param request: Current request. :param bool silent: (default=False) If ``True``, no alert emitted. :rtype: tuple :return: (tuple) A tuple such as ``(action, targets)``. Each submit button returns a string such as ``<act><?|!><target_id>.x`` where ``<target_id>`` is the target identifier or ``#`` for all selected targets, ``<?|!>`` means respectively *confirm* or *proceed* and ``<act>`` is the action to do. Checkbox inputs return string such as ``#<target_id>``. For instance, ``del!#`` and ``['#user1', '#user2']`` means "delete ``user1`` and ``user2``". ``del!user1`` means "delete ``user1`` and only this one". """ action = '' targets = [] for param in request.POST: if param[0] == '#': targets.append(param[1:]) elif param[-2:] == '.x': if param[-3:-2] == '#': action = param[0:-2] else: return param[0:-2], (param[4:-2], ) if '#' in action and not targets: if not silent: request.session.flash(_('Select items!'), 'alert') action = '' return action, tuple(targets)
# =============================================================================
[docs] class SameAs(): # pylint: disable = too-few-public-methods """This class implements a ``colander`` validator to check if to fields are identical. :type request: pyramid.request.Request :param request: Current request. :param str reference: Name of the field to compare with. :type message: :class:`str` or :class:`~pyramid.i18n.TranslationString` :param message: (optional) Error message. """ # ------------------------------------------------------------------------- def __init__( self, request: Request, reference: str, message: str | None = None): """Constructor method.""" self._request = request self._reference = reference self._message = message or _('The two fields are not identical.') # ------------------------------------------------------------------------- def __call__(self, node: colander.SchemaNode, value: str): """This method raises a :class:`colander.Invalid` instance as an exception value is not same as ``self.reference``. :type node: colander.SchemaNode :type value: cstruct """ if self._request.POST.get(self._reference) != value: raise colander.Invalid(node, self._message)
# =============================================================================
[docs] def button( url: str, label: str = '', src: str | None = None, title: str | None = None, class_: str | None = 'cioButton') -> str: """Output a link on a label and an image with a button aspect. :param str url: Target URL. :param str label: (optional) Label for roll over and ``alt``. :param str src: (optional) Image path. :param str title: (optional) Label for roll over. :param str class_: (default='cioButton') The class attribute. :rtype: str """ if class_ == 'cioButton' and not label and src: class_ = None return Literal( '<a href="{0}"{1}{2}>{3}{4}</a> '.format( Literal.escape(url), f' title="{title}"' if title else '', f' class="{class_}"' if class_ else '', f'<img src="{src}" alt="{label or title}"/>' if src else '', label))
# =============================================================================
[docs] def button_icon(url: str, title: str, class_: str = 'cioIconButton') -> str: """Output a link which looks like an icon. :param str url: Target URL. :param str title: Label for roll over. :param str class_: CSS class to give the aspect. :rtype: str """ if 'cioIconButton' not in class_: class_ = f'cioIconButton {class_}' return Literal( f'<a href="{Literal.escape(url)}" title="{title}"' f' class="{class_}"></a> ')
# =============================================================================
[docs] def grid_item( name: str | None, label: str, content: str, required: bool = False, hint: str | None = None, error: str | None = None, title: str | None = None, clear: bool = False, class_: str | None = None) -> str: """Display an item with label, hint and error message. :param str name: Input ID. :param str label: Label. :param str content: HTML content. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str error: (optional) Error message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (optional) The class attribute. :rtype: str This ouputs a structure such as: .. code-block:: html <div class="[class_]"> <label for="[name]"><strong>[label]<span>*</span></strong></label> <tag title="[title]"> [content] <em> [hint]</em> <strong> [form.error(name)]</strong> </tag> <div class="cioClear"></div> </div> """ # pylint: disable = too-many-arguments, too-many-positional-arguments if not content: return '' if error: class_ = f'{class_} cioError' if class_ else 'cioError' return Literal( '<div{class_}>' '<label{name}><strong>{label}{required}</strong></label>' '<div{title}>{content}{hint}{error}</div>{clear}</div>'.format( class_=' class="{0}"'.format(class_) if class_ else '', name=' for="{0}"'.format(name.replace(':', '')) if name else '', label=label or '', required=Builder().span('*') if required else '', title=f' title="{title}"' if title else '', content=content, hint=Builder().em(' {0}'.format(hint)) if hint else '', error=Builder().strong(f' {error}') if error else '', clear=clear and '<div class="cioClear"></div>' or ''))
# =============================================================================
[docs] class Form(): """Form validation class.""" # pylint: disable = too-many-public-methods # ------------------------------------------------------------------------- def __init__( self, request: Request, schema: colander.SchemaNode | None = None, defaults: dict | None = None, obj=None, force_defaults: bool = False): """Constructor method.""" # pylint: disable = too-many-arguments, too-many-positional-arguments self.values = defaults \ if defaults and (not request.POST or force_defaults) else {} self._request = request self._schema = schema self._errors: dict = {} self._special: tuple[list, list] = ([], []) self._validated = False if obj is not None and schema is not None and not request.POST: for field in [k.name for k in schema]: if hasattr(obj, field): self.values[field] = getattr(obj, field) # -------------------------------------------------------------------------
[docs] def validate(self, obj=None) -> bool: """Check if the form is validated. :param object obj: (optional) Object to fill. :rtype: bool :return: ``True`` if validated. """ # Something to do? if not self._request.POST: return False if self._validated: return not self._errors # Schema validation params = dict(self._request.POST.items()) if self._schema: try: self.values = self._schema.deserialize(params) except colander.Invalid as err: self._errors = {} for child in err.children: self._errors[child.node.name] = child.messages() else: self.values.update(params) # Fill object if obj is not None and not self._errors: for field in self.values: if hasattr(obj, field): setattr(obj, field, self.values[field]) self._validated = True return len(self._errors) == 0
# -------------------------------------------------------------------------
[docs] def has_error(self, name: str | None = None) -> bool: """Return ``True`` if field ``name`` has an error. :param str name: (optional) Input ID. :rtype: bool """ return bool(name is None and self._errors) or name in self._errors
# -------------------------------------------------------------------------
[docs] def set_error(self, name: str, message: str): """Set an error message for field ``name``. :param str name: Input ID. :param str message: Error message. """ if name in self._errors: self._errors[name].append(message) else: self._errors[name] = [message]
# -------------------------------------------------------------------------
[docs] def error(self, name: str) -> str: """Return error message for field ``name``. :param str name: Input ID. :rtype: str :return: Translated error message. """ if name not in self._errors: return '' return ' ; '.join([ # yapf: disable self._request.localizer.translate(error) for error in self._errors[name]])
# -------------------------------------------------------------------------
[docs] def static(self, name: str): """The field ``name`` will not be updated by the form. :param str name: Name of field to set static. """ if name not in self._special[0]: self._special[0].append(name)
# -------------------------------------------------------------------------
[docs] def forget(self, prefix: str): """Fields beginning by ``prefix`` are forgotten when the page is refreshed. :param str prefix: Prefix to select fields. """ if prefix not in self._special[1]: self._special[1].append(prefix)
# -------------------------------------------------------------------------
[docs] @classmethod def make_safe_id(cls, name: str) -> str: """Make a string safe for including in an id attribute :param str name: String to transform. :rtype: str """ return re_sub(r'(?!-)\W', '', re_sub( # yapf: disable r'\s', '_', name.replace('?', '0').replace('!', '1'), )).lower()
# -------------------------------------------------------------------------
[docs] def begin( self, url: str | None = None, multipart: bool = False, class_: str | None = 'cioForm', **attrs) -> str: """Ouput the ``<form>`` tag. :param str url: (optional) URL to submit form, by default, the current URL. :param bool multipart: (default=False) If set to ``True``, the enctype is set to ``multipart/form-data``. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ token = get_csrf_token(self._request) or new_csrf_token(self._request) html = tags.form( url or self._request.path_qs, 'post', multipart, class_=class_, **attrs) html += Builder().div( self.hidden('csrf_token', token), class_="cioHidden") return html
# -------------------------------------------------------------------------
[docs] @classmethod def end(cls) -> str: """Ouput the ``</form>`` tag.""" return tags.end_form()
# -------------------------------------------------------------------------
[docs] @classmethod def submit( cls, name: str, label: str | None = None, class_: str = 'cioButton', **attrs) -> str: """Output a submit button with the label as the caption. :param str name: Input ID. :param str label: (optional) Button caption. :param str class_: (default='cioButton') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ return tags.submit( name, label, cls.make_safe_id(name), class_=class_, **attrs)
# -------------------------------------------------------------------------
[docs] @classmethod def submit_image(cls, name: str, label: str, src: str, **attrs) -> str: """Output an image submit button. :param str name: Input ID. :param str label: Label for roll over and ``alt``. :param str src: Image path. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ return Builder().tag( 'input', type='image', name=name, id=cls.make_safe_id(name), src=src, title=label or name, alt=label or name, **attrs)
# -------------------------------------------------------------------------
[docs] @classmethod def submit_icon( cls, name: str, title: str, class_: str = 'cioIconButton', **attrs) -> str: """Output a submit button which looks like an icon. :param str name: Input ID. :param str title: Label for roll over. :param str class_: CSS class to give the aspect. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ if 'cioIconButton' not in class_: class_ = f'cioIconButton {class_}' return Builder().tag( 'button', type='submit', name=f'{name}.x', id=cls.make_safe_id(name), title=title, class_=class_, **attrs)
# -------------------------------------------------------------------------
[docs] @classmethod def submit_cancel_icon(cls, title: str, **attrs) -> str: """Output a cancel submit button which looks like an icon. :param str title: Label for roll over and ``alt``. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ return Builder().tag( 'button', type='submit', name='ccl!.x', id='ccl', title=title, class_='cioIconButton cioButtonCancel', **attrs)
# -------------------------------------------------------------------------
[docs] @classmethod def submit_cancel(cls, label: str, src: str, **attrs) -> str: """Output a cancel submit button. :param str label: Label for roll over and ``alt``. :param str src: Image path. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str :return: HTML tag. """ return Builder().tag( 'input', type='image', name='ccl!', id='ccl', src=src, title=label, alt=label, **attrs)
# -------------------------------------------------------------------------
[docs] def toggle_flag( self, name: str, title: str, active: bool, class_: str = 'cioFlag', **attrs) -> str: """Output a submit button simulating a toggle flag. :param str name: Input ID. :param str title: Flag title. :param bool active: State of the flag._ :param str class_: (default='cioFlag') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ active_str = self._request.localizer.translate( _('active') if active else _('not active')) title = self._request.localizer.translate( # yapf: disable _('${l}: ${a} – click to toggle', {'l': title, 'a': active_str})) class_ = f"cioFlag {class_}{'On' if active else 'Off'}" return tags.submit( f'{name}.x', None, self.make_safe_id(name), title=title, class_=class_, **attrs)
# -------------------------------------------------------------------------
[docs] @classmethod def status(cls, label: str, status: str) -> str: """Output a span showing a status. :param str label: Status label. :param str status: ID of the current status. :rtype: str """ return Literal( f'<span title="{label}" class="cioStatus cioStatus-{status}">' '</span>')
# -------------------------------------------------------------------------
[docs] @classmethod def button( cls, url: str, label: str = '', src: str | None = None, title: str | None = None, class_: str = 'cioButton') -> str: """Output a link on a label and an image with a button aspect. See :func:`button`. """ return button(url, label, src, title, class_)
# -------------------------------------------------------------------------
[docs] @classmethod def button_icon( cls, url: str, title: str, class_: str = 'cioButton') -> str: """Output a link which looks like an icon. See :func:`button_icon`. """ return button_icon(url, title, class_)
# -------------------------------------------------------------------------
[docs] @classmethod def default_button(cls, name: str): """Create an invisible button to catch the Enter signal for the input submit we want to be the default one. :param str name: ID of the input submit to activate. """ return Literal( f'<button type="submit" name="{name}" class="cioDefaultButton"' ' aria-label="Default button"></button>')
# -------------------------------------------------------------------------
[docs] @classmethod def grid_item( cls, label: str, content: str, required: bool = False, hint: str | None = None, error: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem') -> str: """Output an item with label, hint and error message. See :func:`grid_item`. """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( None, label, content, required, hint, error, title, clear, class_)
# -------------------------------------------------------------------------
[docs] def hidden(self, name: str, value: str | None = None, **attrs) -> str: """Output a hidden field. :param str name: Input ID. :param str value: (optional) Hidden value. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ return tags.hidden(name, self._value(name, value), **attrs)
# -------------------------------------------------------------------------
[docs] def text(self, name: str, value: str | None = None, **attrs) -> str: """Output a standard text field. :param str name: Input ID. :param str value: (optional) Default value. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ return tags.text(name, self._value(name, value), **attrs)
# -------------------------------------------------------------------------
[docs] def password(self, name: str, value: str | None = None, **attrs) -> str: """Output a password field. This method takes the same options as text(). """ return tags.password(name, self._value(name, value), **attrs)
# -------------------------------------------------------------------------
[docs] def checkbox( self, name: str, value: str = '1', checked: bool = False, autosubmit: bool = False, **attrs) -> str: """Output a check box. :param str name: Input ID. :param str value: (default='1') The value to return to the application if the box is checked. :param bool checked: (default=False) ``True`` if the box should be initially checked. :param bool autosubmit: (default=False) If ``True``, it adds ``class="cioAutoSubmit"`` attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ if autosubmit: attrs['class_'] = f"{attrs['class_']} cioAutoSubmit" \ if 'class_' in attrs else 'cioAutoSubmit' return tags.checkbox( name, value, checked or self._value(name), **attrs)
# -------------------------------------------------------------------------
[docs] def custom_checkbox( self, name: str, value: str = '1', checked: bool = False, autosubmit: bool = False, class_: str | None = None, **attrs) -> str: """Output a check box followed by an empty label to customize the aspect of the box. :param str name: Input ID. :param str value: (default='1') The value to return to the application if the box is checked. :param bool checked: (default=False) ``True`` if the box should be initially checked. :param bool autosubmit: (default=False) If ``True``, it adds ``class="cioAutoSubmit"`` attribute. :param str class_: (default='cioCustomCheckbox') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ if class_ is None: class_ = 'cioCustomCheckbox' attrs['class_'] = class_ if autosubmit: attrs['class_'] = f"{attrs['class_']} cioAutoSubmit" uid = attrs.get('uid') or self.make_safe_id(name) return Literal( '{0}{1}'.format( tags.checkbox( name, value, checked or bool(self._value(name)), **attrs), f'<label for="{uid}" class="{class_}"> </label>'))
# -------------------------------------------------------------------------
[docs] def radio( self, name: str, value: str, checked: bool = False, autosubmit: bool = False, **attrs) -> str: """Output a radio button. :param str name: Input ID. :param str value: The value to return to the application if the radio is checked. :param bool checked: (default=False) ``True`` if the box should be initially checked. :param bool autosubmit: (default=False) If ``True``, it adds ``class="cioAutoSubmit"`` attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ if autosubmit: attrs['class_'] = '{0} cioAutoSubmit'.format(attrs['class_']) \ if 'class_' in attrs else 'cioAutoSubmit' return tags.radio( name, value, checked or value == self._value(name), **attrs)
# -------------------------------------------------------------------------
[docs] def select( self, name: str, selected_values: str | list[str | int] | None, options: list[str | int | tuple], autosubmit: bool = False, **attrs) -> str: """Output a dropdown selection box. :param str name: Input ID. :type selected_value: :class:`str` or :class:`list` :param selected_value: A string or list of strings or integers giving the value(s) that should be preselected. :type options: (list of :class:`str`, :class:`int` or ``(value, label)`` pairs) :param options: The label will be shown on the form; the option will be returned to the application if that option is chosen. If you pass a ``string`` or ``int`` instead of a ``2-tuple``, it will be used for both the value and the label. :param bool autosubmit: (default=False) If ``True``, it adds ``class="cioAutoSubmit"`` attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ if not options: return '' opts = [] translate = self._request.localizer.translate for opt in options: if isinstance(opt, tuple): opts.append(tags.Option(translate(opt[1]), f'{opt[0]}')) else: opts.append(tags.Option(f'{opt}')) if autosubmit: attrs['class_'] = f"{attrs['class_']} cioAutoSubmit" \ if 'class_' in attrs else 'cioAutoSubmit' return tags.select( name, f'{self._value(name, selected_values)}', tags.Options(opts), **attrs)
# -------------------------------------------------------------------------
[docs] def upload(self, name: str, value=None, **attrs) -> str: """Output a file upload field. :param str name: Input ID. :param str value: (optional) Default value. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ value = self._value(name, value) return tags.file( name, value if isinstance(value, FieldStorage) else None, **attrs)
# -------------------------------------------------------------------------
[docs] def textarea(self, name: str, content: str = '', **attrs) -> str: """Output a text input area. :param str name: Input ID. :param str content: (optional) Default value. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ return tags.textarea(name, self._value(name, content), **attrs)
# -------------------------------------------------------------------------
[docs] def grid_text( self, name: str, label: str, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem', **attrs) -> str: """Output a standard text field in a CSS grid layout. :param str name: Input ID. :param str label: Label. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (default='cioFormItem') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( name, label, self.text(name, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# -------------------------------------------------------------------------
[docs] def grid_password( self, name: str, label: str, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem', **attrs) -> str: """Output a password field in a CSS grid layout. This method takes the same options as grid_text(). """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( name, label, self.password(name, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# -------------------------------------------------------------------------
[docs] def grid_checkbox( self, name: str, label: str, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem', **attrs) -> str: """Output a check box in a CSS grid layout. :param str name: Input ID. :param str label: Label. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (default='cioFormItem') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( name, label, self.checkbox(name, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# -------------------------------------------------------------------------
[docs] def grid_custom_checkbox( self, name: str, label: str, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem', **attrs) -> str: """Output a custom check box in a CSS grid layout. :param str name: Input ID. :param str label: Label. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (default='cioFormItem') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( name, label, self.custom_checkbox(name, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# -------------------------------------------------------------------------
[docs] def grid_select( self, name: str, label: str, options: list[str | int | tuple], autosubmit: bool = False, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_='cioFormItem', **attrs) -> str: """Output a dropdown selection box in a CSS grid layout. :param str name: Input ID. :param str label: Label. :type options: (list of :class:`str`, :class:`int` or ``(value, label)`` pairs) :param options: Values in the dropdown list. :param bool autosubmit: (default=False) If ``True``, it adds ``onchange="submit()"`` attribute. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (default='cioFormItem') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ # pylint: disable = too-many-arguments, too-many-positional-arguments if not options: return '' return grid_item( name, label, self.select(name, None, options, autosubmit, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# -------------------------------------------------------------------------
[docs] def grid_upload( self, name: str, label: str, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem', **attrs) -> str: """Output a file upload field in a CSS grid layout. :param str name: Input ID. :param str label: Label. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (default='cioFormItem') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( name, label, self.upload(name, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# -------------------------------------------------------------------------
[docs] def grid_textarea( self, name: str, label: str, required: bool = False, hint: str | None = None, title: str | None = None, clear: bool = False, class_: str = 'cioFormItem', **attrs) -> str: """Output a text input area in a CSS grid layout. :param str name: Input ID. :param str label: Label. :param bool required: (default=False) Indicate if this field is required. :param str hint: (optional) Help message. :param str title: (optional) Title for the hover effect. :param bool clear: (default=False) If ``True``, add a ``<div class="cioClear"/>`` at the end. :param str class_: (default='cioFormItem') The class attribute. :param dict attrs: Keyworded arguments for ``helpers.tags`` object. :rtype: str """ # pylint: disable = too-many-arguments, too-many-positional-arguments return grid_item( name, label, self.textarea(name, **attrs), required, hint, self.error(name), title=title, clear=clear, class_=class_)
# ------------------------------------------------------------------------- def _value( self, name: str, default: str | list[str | int] | None = None ) -> str | list[str | int] | None: """Return the best value for the field ``name``. :param str name: Input ID. :param str default: (optional) Default value. :rtype: str """ special = name in self._special[0] or [ True for k in self._special[1] if name.startswith(k) ] if not special and name in self._request.POST: return self._request.POST[name] if name in self.values: return self.values[name] return default