diff -r 058bb3dc685f -r 0b59724cb3f2 web/formwidgets.py
--- a/web/formwidgets.py Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1126 +0,0 @@
-# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb. If not, see .
-"""
-Widgets
-~~~~~~~
-
-.. Note::
- A widget is responsible for the display of a field. It may use more than one
- HTML input tags. When the form is posted, a widget is also reponsible to give
- back to the field something it can understand.
-
- Of course you can not use any widget with any field...
-
-.. autoclass:: cubicweb.web.formwidgets.FieldWidget
-
-
-HTML based widgets
-''''''''''''''''''''''''''
-
-.. autoclass:: cubicweb.web.formwidgets.HiddenInput
-.. autoclass:: cubicweb.web.formwidgets.TextInput
-.. autoclass:: cubicweb.web.formwidgets.EmailInput
-.. autoclass:: cubicweb.web.formwidgets.PasswordSingleInput
-.. autoclass:: cubicweb.web.formwidgets.FileInput
-.. autoclass:: cubicweb.web.formwidgets.ButtonInput
-
-
-Other standard HTML widgets
-'''''''''''''''''''''''''''
-
-.. autoclass:: cubicweb.web.formwidgets.TextArea
-.. autoclass:: cubicweb.web.formwidgets.Select
-.. autoclass:: cubicweb.web.formwidgets.CheckBox
-.. autoclass:: cubicweb.web.formwidgets.Radio
-
-
-Date and time widgets
-'''''''''''''''''''''
-
-.. autoclass:: cubicweb.web.formwidgets.DateTimePicker
-.. autoclass:: cubicweb.web.formwidgets.JQueryDateTimePicker
-.. autoclass:: cubicweb.web.formwidgets.JQueryDatePicker
-.. autoclass:: cubicweb.web.formwidgets.JQueryTimePicker
-
-
-Ajax / javascript widgets
-'''''''''''''''''''''''''
-
-.. autoclass:: cubicweb.web.formwidgets.FCKEditor
-.. autoclass:: cubicweb.web.formwidgets.AjaxWidget
-.. autoclass:: cubicweb.web.formwidgets.AutoCompletionWidget
-.. autoclass:: cubicweb.web.formwidgets.InOutWidget
-
-.. kill or document StaticFileAutoCompletionWidget
-.. kill or document LazyRestrictedAutoCompletionWidget
-.. kill or document RestrictedAutoCompletionWidget
-
-
-Other widgets
-'''''''''''''
-
-.. autoclass:: cubicweb.web.formwidgets.PasswordInput
-.. autoclass:: cubicweb.web.formwidgets.IntervalWidget
-.. autoclass:: cubicweb.web.formwidgets.BitSelect
-.. autoclass:: cubicweb.web.formwidgets.HorizontalLayoutWidget
-.. autoclass:: cubicweb.web.formwidgets.EditableURLWidget
-
-
-Form controls
-'''''''''''''
-
-Those classes are not proper widget (they are not associated to field) but are
-used as form controls. Their API is similar to widgets except that `field`
-argument given to :meth:`render` will be `None`.
-
-.. autoclass:: cubicweb.web.formwidgets.Button
-.. autoclass:: cubicweb.web.formwidgets.SubmitButton
-.. autoclass:: cubicweb.web.formwidgets.ResetButton
-.. autoclass:: cubicweb.web.formwidgets.ImgButton
-"""
-__docformat__ = "restructuredtext en"
-
-from functools import reduce
-from datetime import date
-
-from six import text_type, string_types
-
-from logilab.mtconverter import xml_escape
-from logilab.common.date import todatetime
-
-from cubicweb import tags, uilib
-from cubicweb.utils import json_dumps
-from cubicweb.web import stdmsgs, INTERNAL_FIELD_VALUE, ProcessFormError
-
-
-class FieldWidget(object):
- """The abstract base class for widgets.
-
- **Attributes**
-
- Here are standard attributes of a widget, that may be set on concrete class
- to override default behaviours:
-
- :attr:`needs_js`
- list of javascript files needed by the widget.
-
- :attr:`needs_css`
- list of css files needed by the widget.
-
- :attr:`setdomid`
- flag telling if HTML DOM identifier should be set on input.
-
- :attr:`settabindex`
- flag telling if HTML tabindex attribute of inputs should be set.
-
- :attr:`suffix`
- string to use a suffix when generating input, to ease usage as a
- sub-widgets (eg widget used by another widget)
-
- :attr:`vocabulary_widget`
- flag telling if this widget expect a vocabulary
-
- Also, widget instances takes as first argument a `attrs` dictionary which
- will be stored in the attribute of the same name. It contains HTML
- attributes that should be set in the widget's input tag (though concrete
- classes may ignore it).
-
- .. currentmodule:: cubicweb.web.formwidgets
-
- **Form generation methods**
-
- .. automethod:: render
- .. automethod:: _render
- .. automethod:: values
- .. automethod:: attributes
-
- **Post handling methods**
-
- .. automethod:: process_field_data
-
- """
- needs_js = ()
- needs_css = ()
- setdomid = True
- settabindex = True
- suffix = None
- # does this widget expect a vocabulary
- vocabulary_widget = False
-
- def __init__(self, attrs=None, setdomid=None, settabindex=None, suffix=None):
- if attrs is None:
- attrs = {}
- self.attrs = attrs
- if setdomid is not None:
- # override class's default value
- self.setdomid = setdomid
- if settabindex is not None:
- # override class's default value
- self.settabindex = settabindex
- if suffix is not None:
- self.suffix = suffix
-
- def add_media(self, form):
- """adds media (CSS & JS) required by this widget"""
- if self.needs_js:
- form._cw.add_js(self.needs_js)
- if self.needs_css:
- form._cw.add_css(self.needs_css)
-
- def render(self, form, field, renderer=None):
- """Called to render the widget for the given `field` in the given
- `form`. Return a unicode string containing the HTML snippet.
-
- You will usually prefer to override the :meth:`_render` method so you
- don't have to handle addition of needed javascript / css files.
- """
- self.add_media(form)
- return self._render(form, field, renderer)
-
- def _render(self, form, field, renderer):
- """This is the method you have to implement in concrete widget classes.
- """
- raise NotImplementedError()
-
- def format_value(self, form, field, value):
- return field.format_value(form._cw, value)
-
- def attributes(self, form, field):
- """Return HTML attributes for the widget, automatically setting DOM
- identifier and tabindex when desired (see :attr:`setdomid` and
- :attr:`settabindex` attributes)
- """
- attrs = dict(self.attrs)
- if self.setdomid:
- attrs['id'] = field.dom_id(form, self.suffix)
- if self.settabindex and 'tabindex' not in attrs:
- attrs['tabindex'] = form._cw.next_tabindex()
- if 'placeholder' in attrs:
- attrs['placeholder'] = form._cw._(attrs['placeholder'])
- return attrs
-
- def values(self, form, field):
- """Return the current *string* values (i.e. for display in an HTML
- string) for the given field. This method returns a list of values since
- it's suitable for all kind of widgets, some of them taking multiple
- values, but you'll get a single value in the list in most cases.
-
- Those values are searched in:
-
- 1. previously submitted form values if any (on validation error)
-
- 2. req.form (specified using request parameters)
-
- 3. extra form values given to form.render call (specified the code
- generating the form)
-
- 4. field's typed value (returned by its
- :meth:`~cubicweb.web.formfields.Field.typed_value` method)
-
- Values found in 1. and 2. are expected te be already some 'display
- value' (eg a string) while those found in 3. and 4. are expected to be
- correctly typed value.
-
- 3 and 4 are handle by the :meth:`typed_value` method to ease reuse in
- concrete classes.
- """
- values = None
- if not field.ignore_req_params:
- qname = field.input_name(form, self.suffix)
- # value from a previous post that has raised a validation error
- if qname in form.form_previous_values:
- values = form.form_previous_values[qname]
- # value specified using form parameters
- elif qname in form._cw.form:
- values = form._cw.form[qname]
- elif field.name != qname and field.name in form._cw.form:
- # XXX compat: accept attr=value in req.form to specify value of
- # attr-subject
- values = form._cw.form[field.name]
- if values is None:
- values = self.typed_value(form, field)
- if values != INTERNAL_FIELD_VALUE:
- values = self.format_value(form, field, values)
- if not isinstance(values, (tuple, list)):
- values = (values,)
- return values
-
- def typed_value(self, form, field):
- """return field's *typed* value specified in:
- 3. extra form values given to render()
- 4. field's typed value
- """
- qname = field.input_name(form)
- for key in ((field, form), qname):
- try:
- return form.formvalues[key]
- except KeyError:
- continue
- if field.name != qname and field.name in form.formvalues:
- return form.formvalues[field.name]
- return field.typed_value(form)
-
- def process_field_data(self, form, field):
- """Return process posted value(s) for widget and return something
- understandable by the associated `field`. That value may be correctly
- typed or a string that the field may parse.
- """
- posted = form._cw.form
- val = posted.get(field.input_name(form, self.suffix))
- if isinstance(val, string_types):
- val = val.strip()
- return val
-
- # XXX deprecates
- def values_and_attributes(self, form, field):
- return self.values(form, field), self.attributes(form, field)
-
-
-class Input(FieldWidget):
- """abstract widget class for tag based widgets"""
- type = None
-
- def _render(self, form, field, renderer):
- """render the widget for the given `field` of `form`.
-
- Generate one tag for each field's value
- """
- values, attrs = self.values_and_attributes(form, field)
- # ensure something is rendered
- if not values:
- values = (INTERNAL_FIELD_VALUE,)
- inputs = [tags.input(name=field.input_name(form, self.suffix),
- type=self.type, value=value, **attrs)
- for value in values]
- return u'\n'.join(inputs)
-
-
-# basic html widgets ###########################################################
-
-class TextInput(Input):
- """Simple , will return a unicode string."""
- type = 'text'
-
-
-class EmailInput(Input):
- """Simple , will return a unicode string."""
- type = 'email'
-
-
-class PasswordSingleInput(Input):
- """Simple , will return a utf-8 encoded string.
-
- You may prefer using the :class:`~cubicweb.web.formwidgets.PasswordInput`
- widget which handles password confirmation.
- """
- type = 'password'
-
- def process_field_data(self, form, field):
- value = super(PasswordSingleInput, self).process_field_data(form, field)
- if value is not None:
- return value.encode('utf-8')
- return value
-
-
-class PasswordInput(Input):
- """ and a confirmation input. Form processing will
- fail if password and confirmation differs, else it will return the password
- as a utf-8 encoded string.
- """
- type = 'password'
-
- def _render(self, form, field, renderer):
- assert self.suffix is None, 'suffix not supported'
- values, attrs = self.values_and_attributes(form, field)
- assert len(values) == 1
- domid = attrs.pop('id')
- inputs = [tags.input(name=field.input_name(form),
- value=values[0], type=self.type, id=domid, **attrs),
- ' ',
- tags.input(name=field.input_name(form, '-confirm'),
- value=values[0], type=self.type, **attrs),
- ' ', tags.span(form._cw._('confirm password'),
- **{'class': 'emphasis'})]
- return u'\n'.join(inputs)
-
- def process_field_data(self, form, field):
- passwd1 = super(PasswordInput, self).process_field_data(form, field)
- passwd2 = form._cw.form.get(field.input_name(form, '-confirm'))
- if passwd1 == passwd2:
- if passwd1 is None:
- return None
- return passwd1.encode('utf-8')
- raise ProcessFormError(form._cw._("password and confirmation don't match"))
-
-
-class FileInput(Input):
- """Simple , will return a tuple (name, stream) where
- name is the posted file name and stream a file like object containing the
- posted file data.
- """
- type = 'file'
-
- def values(self, form, field):
- # ignore value which makes no sense here (XXX even on form validation error?)
- return ('',)
-
-
-class HiddenInput(Input):
- """Simple for hidden value, will return a unicode
- string.
- """
- type = 'hidden'
- setdomid = False # by default, don't set id attribute on hidden input
- settabindex = False
-
-
-class ButtonInput(Input):
- """Simple , will return a unicode string.
-
- If you want a global form button, look at the :class:`Button`,
- :class:`SubmitButton`, :class:`ResetButton` and :class:`ImgButton` below.
- """
- type = 'button'
-
-
-class TextArea(FieldWidget):
- """Simple