web/formwidgets.py
branchtls-sprint
changeset 1081 f2a85f52b9e5
child 1096 e1fe98850bf7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/formwidgets.py	Wed Mar 11 23:25:29 2009 +0100
@@ -0,0 +1,220 @@
+"""widget classes for form construction
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.common import tags
+
+class FieldWidget(object):
+    needs_js = ()
+    needs_css = ()
+    setdomid = True
+    settabindex = True
+    
+    def __init__(self, attrs=None, setdomid=None, settabindex=None):
+        self.attrs = attrs or {}
+        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
+
+    def add_media(self, form):
+        """adds media (CSS & JS) required by this widget"""
+        req = form.req
+        if self.needs_js:
+            req.add_js(self.needs_js)
+        if self.needs_css:
+            req.add_css(self.needs_css)
+        
+    def render(self, form, field):
+        raise NotImplementedError
+
+    def _render_attrs(self, form, field):
+        name = form.context[field]['name']
+        values = form.context[field]['value']
+        if not isinstance(values, (tuple, list)):
+            values = (values,)
+        attrs = dict(self.attrs)
+        if self.setdomid:
+            attrs['id'] = form.context[field]['id']
+        if self.settabindex:
+            attrs['tabindex'] = form.req.next_tabindex()
+        return name, values, attrs
+
+class Input(FieldWidget):
+    type = None
+    
+    def render(self, form, field):
+        self.add_media(form)
+        name, values, attrs = self._render_attrs(form, field)
+        inputs = [tags.input(name=name, value=value, type=self.type, **attrs)
+                  for value in values]
+        return u'\n'.join(inputs)
+
+class TextInput(Input):
+    type = 'text'
+
+class PasswordInput(Input):
+    type = 'password'
+    
+    def render(self, form, field):
+        self.add_media(form)
+        name, values, attrs = self._render_attrs(form, field)
+        assert len(values) == 1
+        inputs = [tags.input(name=name, value=values[0], type=self.type, **attrs),
+                  '<br/>',
+                  tags.input(name=name+'-confirm', type=self.type, **attrs),
+                  '&nbsp;', tags.span(form.req._('confirm password'),
+                                      **{'class': 'emphasis'})]
+        return u'\n'.join(inputs)
+
+class FileInput(Input):
+    type = 'file'
+    
+    def _render_attrs(self, form, field):
+        # ignore value which makes no sense here (XXX even on form validation error?)
+        name, values, attrs = super(FileInput, self)._render_attrs(form, field)
+        return name, ('',), attrs
+        
+class HiddenInput(Input):
+    type = 'hidden'
+    setdomid = False # by default, don't set id attribute on hidden input
+    settabindex = False
+    
+class ButtonInput(Input):
+    type = 'button'
+
+class TextArea(FieldWidget):
+    def render(self, form, field):
+        name, values, attrs = self._render_attrs(form, field)
+        attrs.setdefault('onkeypress', 'autogrow(this)')
+        if not values:
+            value = u''
+        elif len(values) == 1:
+            value = values[0]
+        else:
+            raise ValueError('a textarea is not supposed to be multivalued')
+        return tags.textarea(value, name=name, **attrs)
+
+
+class FCKEditor(TextArea):
+    def __init__(self, *args, **kwargs):
+        super(FCKEditor, self).__init__(*args, **kwargs)
+        self.attrs['cubicweb:type'] = 'wysiwyg'
+    
+    def render(self, form, field):
+        form.req.fckeditor_config()
+        return super(FCKEditor, self).render(form, field)
+
+
+class Select(FieldWidget):
+    def __init__(self, attrs=None, multiple=False):
+        super(Select, self).__init__(attrs)
+        self.multiple = multiple
+        
+    def render(self, form, field):
+        name, curvalues, attrs = self._render_attrs(form, field)
+        options = []
+        for label, value in field.vocabulary(form):
+            if value in curvalues:
+                options.append(tags.option(label, value=value, selected='selected'))
+            else:
+                options.append(tags.option(label, value=value))
+        return tags.select(name=name, multiple=self.multiple,
+                           options=options, **attrs)
+
+
+class CheckBox(Input):
+    type = 'checkbox'
+    
+    def render(self, form, field):
+        name, curvalues, attrs = self._render_attrs(form, field)
+        options = []
+        for label, value in field.vocabulary(form):
+            if value in curvalues:
+                tag = tags.input(name=name, value=value, type=self.type,
+                                 checked='checked', **attrs)
+            else:
+                tag = tags.input(name=name, value=value, type=self.type,
+                                 **attrs)
+            options.append(tag + label)
+        return '<br/>\n'.join(options)
+
+        
+class Radio(Input):
+    type = 'radio'
+    setdomid = False
+    
+    def render(self, form, field):
+        name, curvalues, attrs = self._render_attrs(form, field)
+        options = []
+        for label, value in field.vocabulary(form):
+            if value in curvalues:
+                options.append(tags.input(name=name, type=self.type, value=value, checked='checked', **attrs))
+            else:
+                options.append(tags.option(name=name, type=self.type, value=value, **attrs))
+            options[-1] += label + '<br/>'
+        return '\n'.join(options)
+
+
+class DateTimePicker(TextInput):
+    monthnames = ('january', 'february', 'march', 'april',
+                  'may', 'june', 'july', 'august',
+                  'september', 'october', 'november', 'december')
+    daynames = ('monday', 'tuesday', 'wednesday', 'thursday',
+                'friday', 'saturday', 'sunday')
+
+    needs_js = ('cubicweb.ajax.js', 'cubicweb.calendar.js')
+    needs_css = ('cubicweb.calendar_popup.css',)
+    
+    @classmethod
+    def add_localized_infos(cls, req):
+        """inserts JS variables defining localized months and days"""
+        # import here to avoid dependancy from cubicweb-common to simplejson
+        _ = req._
+        monthnames = [_(mname) for mname in cls.monthnames]
+        daynames = [_(dname) for dname in cls.daynames]
+        req.html_headers.define_var('MONTHNAMES', monthnames)
+        req.html_headers.define_var('DAYNAMES', daynames)
+    
+    def render(self, form, field):
+        txtwidget = super(DateTimePicker, self).render(form, field)
+        self.add_localized_infos(form.req)
+        cal_button = self._render_calendar_popup(form, field)
+        return txtwidget + cal_button
+    
+    def _render_calendar_popup(self, form, field):
+        req = form.req
+        value = form.context[field]['rawvalue']
+        inputid = form.context[field]['id']
+        helperid = '%shelper' % inputid
+        if not value:
+            value = date.today()
+        year, month = value.year, value.month
+        onclick = "toggleCalendar('%s', '%s', %s, %s);" % (
+            helperid, inputid, year, month)
+        return (u"""<a onclick="toggleCalendar('%s', '%s', %s, %s);" class="calhelper">
+<img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""
+                % (helperid, inputid, year, month,
+                   req.external_resource('CALENDAR_ICON'),
+                   req._('calendar'), helperid) )
+
+
+class AjaxWidget(FieldWidget):
+    def __init__(self, wdgtype, inputid=None, **kwargs):
+        super(AjaxWidget, self).__init__(**kwargs)
+        self.attrs.setdefault('class', 'widget')
+        self.attrs.setdefault('cubicweb:loadtype', 'auto')
+        self.attrs['cubicweb:wdgtype'] = wdgtype
+        if inputid is not None:
+            self.attrs['cubicweb:inputid'] = inputid
+            
+    def render(self, form, field):
+        self.add_media(form)
+        name, values, attrs = self._render_attrs(form, field)
+        return tags.div(**attrs)