web/formwidgets.py
branchtls-sprint
changeset 1081 f2a85f52b9e5
child 1096 e1fe98850bf7
equal deleted inserted replaced
1074:c07f3accf04a 1081:f2a85f52b9e5
       
     1 """widget classes for form construction
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from cubicweb.common import tags
       
    10 
       
    11 class FieldWidget(object):
       
    12     needs_js = ()
       
    13     needs_css = ()
       
    14     setdomid = True
       
    15     settabindex = True
       
    16     
       
    17     def __init__(self, attrs=None, setdomid=None, settabindex=None):
       
    18         self.attrs = attrs or {}
       
    19         if setdomid is not None:
       
    20             # override class's default value
       
    21             self.setdomid = setdomid
       
    22         if settabindex is not None:
       
    23             # override class's default value
       
    24             self.settabindex = settabindex
       
    25 
       
    26     def add_media(self, form):
       
    27         """adds media (CSS & JS) required by this widget"""
       
    28         req = form.req
       
    29         if self.needs_js:
       
    30             req.add_js(self.needs_js)
       
    31         if self.needs_css:
       
    32             req.add_css(self.needs_css)
       
    33         
       
    34     def render(self, form, field):
       
    35         raise NotImplementedError
       
    36 
       
    37     def _render_attrs(self, form, field):
       
    38         name = form.context[field]['name']
       
    39         values = form.context[field]['value']
       
    40         if not isinstance(values, (tuple, list)):
       
    41             values = (values,)
       
    42         attrs = dict(self.attrs)
       
    43         if self.setdomid:
       
    44             attrs['id'] = form.context[field]['id']
       
    45         if self.settabindex:
       
    46             attrs['tabindex'] = form.req.next_tabindex()
       
    47         return name, values, attrs
       
    48 
       
    49 class Input(FieldWidget):
       
    50     type = None
       
    51     
       
    52     def render(self, form, field):
       
    53         self.add_media(form)
       
    54         name, values, attrs = self._render_attrs(form, field)
       
    55         inputs = [tags.input(name=name, value=value, type=self.type, **attrs)
       
    56                   for value in values]
       
    57         return u'\n'.join(inputs)
       
    58 
       
    59 class TextInput(Input):
       
    60     type = 'text'
       
    61 
       
    62 class PasswordInput(Input):
       
    63     type = 'password'
       
    64     
       
    65     def render(self, form, field):
       
    66         self.add_media(form)
       
    67         name, values, attrs = self._render_attrs(form, field)
       
    68         assert len(values) == 1
       
    69         inputs = [tags.input(name=name, value=values[0], type=self.type, **attrs),
       
    70                   '<br/>',
       
    71                   tags.input(name=name+'-confirm', type=self.type, **attrs),
       
    72                   '&nbsp;', tags.span(form.req._('confirm password'),
       
    73                                       **{'class': 'emphasis'})]
       
    74         return u'\n'.join(inputs)
       
    75 
       
    76 class FileInput(Input):
       
    77     type = 'file'
       
    78     
       
    79     def _render_attrs(self, form, field):
       
    80         # ignore value which makes no sense here (XXX even on form validation error?)
       
    81         name, values, attrs = super(FileInput, self)._render_attrs(form, field)
       
    82         return name, ('',), attrs
       
    83         
       
    84 class HiddenInput(Input):
       
    85     type = 'hidden'
       
    86     setdomid = False # by default, don't set id attribute on hidden input
       
    87     settabindex = False
       
    88     
       
    89 class ButtonInput(Input):
       
    90     type = 'button'
       
    91 
       
    92 class TextArea(FieldWidget):
       
    93     def render(self, form, field):
       
    94         name, values, attrs = self._render_attrs(form, field)
       
    95         attrs.setdefault('onkeypress', 'autogrow(this)')
       
    96         if not values:
       
    97             value = u''
       
    98         elif len(values) == 1:
       
    99             value = values[0]
       
   100         else:
       
   101             raise ValueError('a textarea is not supposed to be multivalued')
       
   102         return tags.textarea(value, name=name, **attrs)
       
   103 
       
   104 
       
   105 class FCKEditor(TextArea):
       
   106     def __init__(self, *args, **kwargs):
       
   107         super(FCKEditor, self).__init__(*args, **kwargs)
       
   108         self.attrs['cubicweb:type'] = 'wysiwyg'
       
   109     
       
   110     def render(self, form, field):
       
   111         form.req.fckeditor_config()
       
   112         return super(FCKEditor, self).render(form, field)
       
   113 
       
   114 
       
   115 class Select(FieldWidget):
       
   116     def __init__(self, attrs=None, multiple=False):
       
   117         super(Select, self).__init__(attrs)
       
   118         self.multiple = multiple
       
   119         
       
   120     def render(self, form, field):
       
   121         name, curvalues, attrs = self._render_attrs(form, field)
       
   122         options = []
       
   123         for label, value in field.vocabulary(form):
       
   124             if value in curvalues:
       
   125                 options.append(tags.option(label, value=value, selected='selected'))
       
   126             else:
       
   127                 options.append(tags.option(label, value=value))
       
   128         return tags.select(name=name, multiple=self.multiple,
       
   129                            options=options, **attrs)
       
   130 
       
   131 
       
   132 class CheckBox(Input):
       
   133     type = 'checkbox'
       
   134     
       
   135     def render(self, form, field):
       
   136         name, curvalues, attrs = self._render_attrs(form, field)
       
   137         options = []
       
   138         for label, value in field.vocabulary(form):
       
   139             if value in curvalues:
       
   140                 tag = tags.input(name=name, value=value, type=self.type,
       
   141                                  checked='checked', **attrs)
       
   142             else:
       
   143                 tag = tags.input(name=name, value=value, type=self.type,
       
   144                                  **attrs)
       
   145             options.append(tag + label)
       
   146         return '<br/>\n'.join(options)
       
   147 
       
   148         
       
   149 class Radio(Input):
       
   150     type = 'radio'
       
   151     setdomid = False
       
   152     
       
   153     def render(self, form, field):
       
   154         name, curvalues, attrs = self._render_attrs(form, field)
       
   155         options = []
       
   156         for label, value in field.vocabulary(form):
       
   157             if value in curvalues:
       
   158                 options.append(tags.input(name=name, type=self.type, value=value, checked='checked', **attrs))
       
   159             else:
       
   160                 options.append(tags.option(name=name, type=self.type, value=value, **attrs))
       
   161             options[-1] += label + '<br/>'
       
   162         return '\n'.join(options)
       
   163 
       
   164 
       
   165 class DateTimePicker(TextInput):
       
   166     monthnames = ('january', 'february', 'march', 'april',
       
   167                   'may', 'june', 'july', 'august',
       
   168                   'september', 'october', 'november', 'december')
       
   169     daynames = ('monday', 'tuesday', 'wednesday', 'thursday',
       
   170                 'friday', 'saturday', 'sunday')
       
   171 
       
   172     needs_js = ('cubicweb.ajax.js', 'cubicweb.calendar.js')
       
   173     needs_css = ('cubicweb.calendar_popup.css',)
       
   174     
       
   175     @classmethod
       
   176     def add_localized_infos(cls, req):
       
   177         """inserts JS variables defining localized months and days"""
       
   178         # import here to avoid dependancy from cubicweb-common to simplejson
       
   179         _ = req._
       
   180         monthnames = [_(mname) for mname in cls.monthnames]
       
   181         daynames = [_(dname) for dname in cls.daynames]
       
   182         req.html_headers.define_var('MONTHNAMES', monthnames)
       
   183         req.html_headers.define_var('DAYNAMES', daynames)
       
   184     
       
   185     def render(self, form, field):
       
   186         txtwidget = super(DateTimePicker, self).render(form, field)
       
   187         self.add_localized_infos(form.req)
       
   188         cal_button = self._render_calendar_popup(form, field)
       
   189         return txtwidget + cal_button
       
   190     
       
   191     def _render_calendar_popup(self, form, field):
       
   192         req = form.req
       
   193         value = form.context[field]['rawvalue']
       
   194         inputid = form.context[field]['id']
       
   195         helperid = '%shelper' % inputid
       
   196         if not value:
       
   197             value = date.today()
       
   198         year, month = value.year, value.month
       
   199         onclick = "toggleCalendar('%s', '%s', %s, %s);" % (
       
   200             helperid, inputid, year, month)
       
   201         return (u"""<a onclick="toggleCalendar('%s', '%s', %s, %s);" class="calhelper">
       
   202 <img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""
       
   203                 % (helperid, inputid, year, month,
       
   204                    req.external_resource('CALENDAR_ICON'),
       
   205                    req._('calendar'), helperid) )
       
   206 
       
   207 
       
   208 class AjaxWidget(FieldWidget):
       
   209     def __init__(self, wdgtype, inputid=None, **kwargs):
       
   210         super(AjaxWidget, self).__init__(**kwargs)
       
   211         self.attrs.setdefault('class', 'widget')
       
   212         self.attrs.setdefault('cubicweb:loadtype', 'auto')
       
   213         self.attrs['cubicweb:wdgtype'] = wdgtype
       
   214         if inputid is not None:
       
   215             self.attrs['cubicweb:inputid'] = inputid
       
   216             
       
   217     def render(self, form, field):
       
   218         self.add_media(form)
       
   219         name, values, attrs = self._render_attrs(form, field)
       
   220         return tags.div(**attrs)