diff -r 4176a50c81c9 -r d321e4b62a10 web/formwidgets.py --- a/web/formwidgets.py Wed Apr 21 16:53:25 2010 +0200 +++ b/web/formwidgets.py Wed Apr 21 16:53:47 2010 +0200 @@ -1,9 +1,75 @@ -"""widget classes for form construction +# organization: Logilab +# copyright: 2009-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. +# contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr +# license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses +""" +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.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 -:organization: Logilab -:copyright: 2009-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. -:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr -:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses +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 + +.. kill or document AddComboBoxWidget +.. 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.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" @@ -19,14 +85,50 @@ class FieldWidget(object): - """abstract widget class""" - # javascript / css files required by the widget + """The abstract base class for widgets. + + **Attributes** + + Here are standard attributes of a widget, that may be set on concret + 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 concret + 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 = () - # automatically set id and tabindex attributes ? setdomid = True settabindex = True - # to ease usage as a sub-widgets (eg widget used by another widget) suffix = None # does this widget expect a vocabulary vocabulary_widget = False @@ -51,12 +153,19 @@ 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. - def render(self, form, field, renderer=None): + 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 concret widget classes. + """ raise NotImplementedError() def format_value(self, form, field, value): @@ -75,6 +184,30 @@ 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 + concret classes. + """ values = None if not field.ignore_req_params: qname = field.input_name(form, self.suffix) @@ -112,6 +245,10 @@ 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, basestring): @@ -152,13 +289,29 @@ # basic html widgets ########################################################### class TextInput(Input): - """""" + """Simple , will return an unicode string.""" type = 'text' +class PasswordSingleInput(Input): + """Simple , will return an 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 its confirmation field (using - -confirm as name) + """ and a confirmation input. Form processing will + fail if password and confirmation differs, else it will return the password + as an utf-8 encoded string. """ type = 'password' @@ -186,19 +339,11 @@ raise ProcessFormError(form._cw._("password and confirmation don't match")) -class PasswordSingleInput(Input): - """ without a confirmation field""" - 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 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): @@ -207,23 +352,25 @@ class HiddenInput(Input): - """""" + """Simple for hidden value, will return an unicode + string. + """ type = 'hidden' setdomid = False # by default, don't set id attribute on hidden input settabindex = False class ButtonInput(Input): - """ + """Simple , will return an unicode string. - if you want a global form button, look at the Button, SubmitButton, - ResetButton and ImgButton classes below. + 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): - """