web/widgets.py
branchstable
changeset 4556 43c14e0e8972
parent 4555 8968c50818db
parent 4553 23201259ffeb
child 4560 23e0632df615
--- a/web/widgets.py	Fri Feb 12 12:57:14 2010 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,930 +0,0 @@
-"""widgets for entity edition
-
-those are in cubicweb.common since we need to know available widgets at schema
-serialization time
-
-:organization: Logilab
-:copyright: 2001-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
-"""
-__docformat__ = "restructuredtext en"
-
-from datetime import datetime
-
-from logilab.mtconverter import xml_escape
-
-from yams.constraints import SizeConstraint, StaticVocabularyConstraint
-
-from cubicweb.common.uilib import toggle_action
-from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param
-
-def _format_attrs(kwattrs):
-    """kwattrs is the dictionary of the html attributes available for
-    the edited element
-    """
-    # sort for predictability (required for tests)
-    return u' '.join(sorted(u'%s="%s"' % item for item in kwattrs.iteritems()))
-
-def _value_from_values(values):
-    # take care, value may be 0, 0.0...
-    if values:
-        value = values[0]
-        if value is None:
-            value = u''
-    else:
-        value = u''
-    return value
-
-def _eclass_eschema(eschema_or_eclass):
-    try:
-        return eschema_or_eclass, eschema_or_eclass.e_schema
-    except AttributeError:
-        return None, eschema_or_eclass
-
-def checkbox(name, value, attrs='', checked=None):
-    if checked is None:
-        checked = value
-    checked = checked and 'checked="checked"' or ''
-    return u'<input type="checkbox" name="%s" value="%s" %s %s />' % (
-        name, value, checked, attrs)
-
-def widget(vreg, subjschema, rschema, objschema, role='object'):
-    """get a widget to edit the given relation"""
-    if rschema == 'eid':
-        # return HiddenWidget(vreg, subjschema, rschema, objschema)
-        return EidWidget(vreg, _eclass_eschema(subjschema)[1], rschema, objschema)
-    return widget_factory(vreg, subjschema, rschema, objschema, role=role)
-
-
-class Widget(object):
-    """abstract widget class"""
-    need_multipart = False
-    # generate the "id" attribute with the same value as the "name" (html) attribute
-    autoid = True
-    html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress'))
-    cubicwebns_attributes = set()
-
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 role='subject', description=None,
-                 **kwattrs):
-        self.vreg = vreg
-        self.rschema = rschema
-        self.subjtype = subjschema
-        self.objtype = objschema
-        self.role = role
-        self.name = rschema.type
-        self.description = description
-        self.attrs = kwattrs
-        # XXX accesskey may not be unique
-        kwattrs['accesskey'] = self.name[0]
-
-    def copy(self):
-        """shallow copy (useful when you need to modify self.attrs
-        because widget instances are cached)
-        """
-        # brute force copy (subclasses don't have the
-        # same __init__ prototype)
-        widget = self.__new__(self.__class__)
-        widget.__dict__ = dict(self.__dict__)
-        widget.attrs = dict(widget.attrs)
-        return widget
-
-    @staticmethod
-    def size_constraint_attrs(attrs, maxsize):
-        """set html attributes in the attrs dict to consider maxsize"""
-        pass
-
-    def format_attrs(self):
-        """return a string with html attributes available for the edit input"""
-        # sort for predictability (required for tests)
-        attrs = []
-        for name, value in self.attrs.iteritems():
-            # namespace attributes have priority over standard xhtml ones
-            if name in self.cubicwebns_attributes:
-                attrs.append(u'cubicweb:%s="%s"' % (name, value))
-            elif name in self.html_attributes:
-                attrs.append(u'%s="%s"' % (name, value))
-        return u' '.join(sorted(attrs))
-
-    def required(self, entity):
-        """indicates if the widget needs a value to be filled in"""
-        card = self.rschema.cardinality(self.subjtype, self.objtype, self.role)
-        return card in '1+'
-
-    def input_id(self, entity):
-        try:
-            return self.rname
-        except AttributeError:
-            return eid_param(self.name, entity.eid)
-
-    def render_label(self, entity, label=None):
-        """render widget's label"""
-        label = label or self.rschema.display_name(entity.req, self.role)
-        forid = self.input_id(entity)
-        if forid:
-            forattr =  ' for="%s"' % forid
-        else:
-            forattr = ''
-        if self.required(entity):
-            label = u'<label class="required"%s>%s</label>' % (forattr, label)
-        else:
-            label = u'<label%s>%s</label>' % (forattr, label)
-        return label
-
-    def render_error(self, entity):
-        """return validation error for widget's field of the given entity, if
-        any
-        """
-        errex = entity.req.data.get('formerrors')
-        if errex and errex.eid == entity.eid and self.name in errex.errors:
-            entity.req.data['displayederrors'].add(self.name)
-            return u'<span class="error">%s</span>' % errex.errors[self.name]
-        return u''
-
-    def render_help(self, entity):
-        """render a help message about the (edited) field"""
-        req = entity.req
-        help = [u'<div class="helper">']
-        descr = self.description or self.rschema.rproperty(self.subjtype, self.objtype, 'description')
-        if descr:
-            help.append(u'<span>%s</span>' % req._(descr))
-        example = self.render_example(req)
-        if example:
-            help.append(u'<span>(%s: %s)</span>'
-                        % (req._('sample format'), example))
-        help.append(u'</div>')
-        return u'&#160;'.join(help)
-
-    def render_example(self, req):
-        return u''
-
-    def render(self, entity):
-        """render the widget for a simple view"""
-        if not entity.has_eid():
-            return u''
-        return entity.printable_value(self.name)
-
-    def edit_render(self, entity, tabindex=None,
-                    includehelp=False, useid=None, **kwargs):
-        """render the widget for edition"""
-        # this is necessary to handle multiple edition
-        self.rname = eid_param(self.name, entity.eid)
-        if useid:
-            self.attrs['id'] = useid
-        elif self.autoid:
-            self.attrs['id'] = self.rname
-        if tabindex is not None:
-            self.attrs['tabindex'] = tabindex
-        else:
-            self.attrs['tabindex'] = entity.req.next_tabindex()
-        output = self._edit_render(entity, **kwargs)
-        if includehelp:
-            output += self.render_help(entity)
-        return output
-
-    def _edit_render(self, entity):
-        """do the actual job to render the widget for edition"""
-        raise NotImplementedError
-
-    def current_values(self, entity):
-        """return the value of the field associated to this widget on the given
-        entity. always return a list of values, which'll have size equal to 1
-        if the field is monovalued (like all attribute fields, but not all non
-        final relation fields
-        """
-        if self.rschema.final:
-            return entity.attribute_values(self.name)
-        elif entity.has_eid():
-            return [row[0] for row in entity.related(self.name, self.role)]
-        return ()
-
-    def current_value(self, entity):
-        return _value_from_values(self.current_values(entity))
-
-    def current_display_values(self, entity):
-        """same as .current_values but consider values stored in session in case
-        of validation error
-        """
-        values = entity.req.data.get('formvalues')
-        if values is None:
-            return self.current_values(entity)
-        cdvalues = values.get(self.rname)
-        if cdvalues is None:
-            return self.current_values(entity)
-        if not isinstance(cdvalues, (list, tuple)):
-            cdvalues = (cdvalues,)
-        return cdvalues
-
-    def current_display_value(self, entity):
-        """same as .current_value but consider values stored in session in case
-        of validation error
-        """
-        return _value_from_values(self.current_display_values(entity))
-
-    def hidden_input(self, entity, qvalue):
-        """return an hidden field which
-        1. indicates that a field is edited
-        2. hold the old value to easily detect if the field has been modified
-
-        `qvalue` is the html quoted old value
-        """
-        if self.role == 'subject':
-            editmark = 'edits'
-        else:
-            editmark = 'edito'
-        if qvalue is None or not entity.has_eid():
-            qvalue = INTERNAL_FIELD_VALUE
-        return u'<input type="hidden" name="%s-%s" value="%s"/>\n' % (
-            editmark, self.rname, qvalue)
-
-class InputWidget(Widget):
-    """abstract class for input generating a <input> tag"""
-    input_type = None
-    html_attributes = Widget.html_attributes | set(('type', 'name', 'value'))
-
-    def _edit_render(self, entity):
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        if isinstance(value, basestring):
-            value = xml_escape(value)
-        if isinstance(dvalue, basestring):
-            dvalue = xml_escape(dvalue)
-        return u'%s<input type="%s" name="%s" value="%s" %s/>' % (
-            self.hidden_input(entity, value), self.input_type,
-            self.rname, dvalue, self.format_attrs())
-
-class HiddenWidget(InputWidget):
-    input_type = 'hidden'
-    autoid = False
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 role='subject', **kwattrs):
-        InputWidget.__init__(self, vreg, subjschema, rschema, objschema,
-                             role='subject',
-                             **kwattrs)
-        # disable access key
-        del self.attrs['accesskey']
-
-    def current_value(self, entity):
-        value = InputWidget.current_value(self, entity)
-        return value or INTERNAL_FIELD_VALUE
-
-    def current_display_value(self, entity):
-        value = InputWidget.current_display_value(self, entity)
-        return value or INTERNAL_FIELD_VALUE
-
-    def render_label(self, entity, label=None):
-        """render widget's label"""
-        return u''
-
-    def render_help(self, entity):
-        return u''
-
-    def hidden_input(self, entity, value):
-        """no hidden input for hidden input"""
-        return ''
-
-
-class EidWidget(HiddenWidget):
-
-    def _edit_render(self, entity):
-        return u'<input type="hidden" name="eid" value="%s" />' % entity.eid
-
-
-class StringWidget(InputWidget):
-    input_type = 'text'
-    html_attributes = InputWidget.html_attributes | set(('size', 'maxlength'))
-    @staticmethod
-    def size_constraint_attrs(attrs, maxsize):
-        """set html attributes in the attrs dict to consider maxsize"""
-        attrs['size'] = min(maxsize, 40)
-        attrs['maxlength'] = maxsize
-
-
-class AutoCompletionWidget(StringWidget):
-    cubicwebns_attributes = (StringWidget.cubicwebns_attributes |
-                          set(('accesskey', 'size', 'maxlength')))
-    attrs = ()
-
-    wdgtype = 'SuggestField'
-
-    def current_value(self, entity):
-        value = StringWidget.current_value(self, entity)
-        return value or INTERNAL_FIELD_VALUE
-
-    def _get_url(self, entity):
-        return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema],
-                                pageid=entity.req.pageid, mode='remote')
-
-    def _edit_render(self, entity):
-        req = entity.req
-        req.add_js( ('cubicweb.widgets.js', 'jquery.autocomplete.js') )
-        req.add_css('jquery.autocomplete.css')
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        if isinstance(value, basestring):
-            value = xml_escape(value)
-        if isinstance(dvalue, basestring):
-            dvalue = xml_escape(dvalue)
-        iid = self.attrs.pop('id')
-        if self.required(entity):
-            cssclass = u' required'
-        else:
-            cssclass = u''
-        dataurl = self._get_url(entity)
-        return (u'%(hidden)s<input type="text" name="%(iid)s" value="%(value)s" cubicweb:dataurl="%(url)s" class="widget%(required)s" id="%(iid)s" '
-                u'tabindex="%(tabindex)s" cubicweb:loadtype="auto" cubicweb:wdgtype="%(wdgtype)s"  %(attrs)s />' % {
-                    'iid': iid,
-                    'hidden': self.hidden_input(entity, value),
-                    'wdgtype': self.wdgtype,
-                    'url': xml_escape(dataurl),
-                    'tabindex': self.attrs.pop('tabindex'),
-                    'value': dvalue,
-                    'attrs': self.format_attrs(),
-                    'required' : cssclass,
-                    })
-
-class StaticFileAutoCompletionWidget(AutoCompletionWidget):
-    wdgtype = 'StaticFileSuggestField'
-
-    def _get_url(self, entity):
-        return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
-
-class RestrictedAutoCompletionWidget(AutoCompletionWidget):
-    wdgtype = 'RestrictedSuggestField'
-
-
-class PasswordWidget(InputWidget):
-    input_type = 'password'
-
-    def required(self, entity):
-        if InputWidget.required(self, entity) and not entity.has_eid():
-            return True
-        return False
-
-    def current_values(self, entity):
-        # on existant entity, show password field has non empty (we don't have
-        # the actual value
-        if entity.has_eid():
-            return (INTERNAL_FIELD_VALUE,)
-        return super(PasswordWidget, self).current_values(entity)
-
-    def _edit_render(self, entity):
-        html = super(PasswordWidget, self)._edit_render(entity)
-        name = eid_param(self.name + '-confirm', entity.eid)
-        return u'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/>&#160;<span class="emphasis">(%s)</span>' % (
-            html, self.input_type, name, name, entity.req.next_tabindex(),
-            entity.req._('confirm password'))
-
-
-class TextWidget(Widget):
-    html_attributes = Widget.html_attributes | set(('rows', 'cols'))
-
-    @staticmethod
-    def size_constraint_attrs(attrs, maxsize):
-        """set html attributes in the attrs dict to consider maxsize"""
-        if 256 < maxsize < 513:
-            attrs['cols'], attrs['rows'] = 60, 5
-        else:
-            attrs['cols'], attrs['rows'] = 80, 10
-
-    def render(self, entity):
-        if not entity.has_eid():
-            return u''
-        return entity.printable_value(self.name)
-
-    def _edit_render(self, entity, with_format=True):
-        req = entity.req
-        editor = self._edit_render_textarea(entity, with_format)
-        value = self.current_value(entity)
-        if isinstance(value, basestring):
-            value = xml_escape(value)
-        return u'%s%s' % (self.hidden_input(entity, value), editor)
-
-    def _edit_render_textarea(self, entity, with_format):
-        self.attrs.setdefault('cols', 80)
-        self.attrs.setdefault('rows', 20)
-        dvalue = self.current_display_value(entity)
-        if isinstance(dvalue, basestring):
-            dvalue = xml_escape(dvalue)
-        if entity.use_fckeditor(self.name):
-            entity.req.fckeditor_config()
-            if with_format:
-                if entity.has_eid():
-                    format = entity.attr_metadata(self.name, 'format')
-                else:
-                    format = ''
-                frname = eid_param(self.name + '_format', entity.eid)
-                hidden = u'<input type="hidden" name="edits-%s" value="%s"/>\n'\
-                         '<input type="hidden" name="%s" value="text/html"/>\n' % (
-                    frname, format, frname)
-            return u'%s<textarea cubicweb:type="wysiwyg" onkeyup="autogrow(this)" name="%s" %s>%s</textarea>' % (
-                hidden, self.rname, self.format_attrs(), dvalue)
-        if with_format and entity.e_schema.has_metadata(self.name, 'format'):
-            fmtwdg = entity.get_widget(self.name + '_format')
-            fmtwdgstr = fmtwdg.edit_render(entity, tabindex=self.attrs['tabindex'])
-            self.attrs['tabindex'] = entity.req.next_tabindex()
-        else:
-            fmtwdgstr = ''
-        return u'%s<br/><textarea onkeyup="autogrow(this)" name="%s" %s>%s</textarea>' % (
-            fmtwdgstr, self.rname, self.format_attrs(), dvalue)
-
-
-class CheckBoxWidget(Widget):
-    html_attributes = Widget.html_attributes | set(('checked', ))
-    def _edit_render(self, entity):
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        return self.hidden_input(entity, value) + checkbox(self.rname, 'checked', self.format_attrs(), dvalue)
-
-    def render(self, entity):
-        if not entity.has_eid():
-            return u''
-        if getattr(entity, self.name):
-            return entity.req._('yes')
-        return entity.req._('no')
-
-
-class YesNoRadioWidget(CheckBoxWidget):
-    html_attributes = Widget.html_attributes | set(('disabled',))
-    def _edit_render(self, entity):
-        value = self.current_value(entity)
-        dvalue = self.current_display_value(entity)
-        attrs1 = self.format_attrs()
-        del self.attrs['id'] # avoid duplicate id for xhtml compliance
-        attrs2 = self.format_attrs()
-        if dvalue:
-            attrs1 += ' checked="checked"'
-        else:
-            attrs2 += ' checked="checked"'
-        wdgs = [self.hidden_input(entity, value),
-                u'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')),
-                u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))]
-        return '\n'.join(wdgs)
-
-
-class FileWidget(Widget):
-    need_multipart = True
-    def _file_wdg(self, entity):
-        wdgs = [u'<input type="file" name="%s" %s/>' % (self.rname, self.format_attrs())]
-        req = entity.req
-        if (entity.e_schema.has_metadata(self.name, 'format')
-            or entity.e_schema.has_metadata(self.name, 'encoding')):
-            divid = '%s-%s-advanced' % (self.name, entity.eid)
-            wdgs.append(u'<a href="%s" title="%s"><img src="%s" alt="%s"/></a>' %
-                        (xml_escape(toggle_action(divid)),
-                         req._('show advanced fields'),
-                         xml_escape(req.build_url('data/puce_down.png')),
-                         req._('show advanced fields')))
-            wdgs.append(u'<div id="%s" class="hidden">' % divid)
-            for extraattr in ('_format', '_encoding'):
-                if entity.e_schema.has_subject_relation('%s%s' % (self.name, extraattr)):
-                    ewdg = entity.get_widget(self.name + extraattr)
-                    wdgs.append(ewdg.render_label(entity))
-                    wdgs.append(ewdg.edit_render(entity, includehelp=True))
-                    wdgs.append(u'<br/>')
-            wdgs.append(u'</div>')
-        if entity.has_eid():
-            if not self.required(entity):
-                # trick to be able to delete an uploaded file
-                wdgs.append(u'<br/>')
-                wdgs.append(checkbox(eid_param('__%s_detach' % self.rname, entity.eid), False))
-                wdgs.append(req._('detach attached file %s' % entity.dc_title()))
-            else:
-                wdgs.append(u'<br/>')
-                wdgs.append(req._('currently attached file: %s' % entity.dc_title()))
-        return '\n'.join(wdgs)
-
-    def _edit_render(self, entity):
-        return self.hidden_input(entity, None) + self._file_wdg(entity)
-
-
-class TextFileWidget(FileWidget):
-    def _edit_msg(self, entity):
-        if entity.has_eid() and not self.required(entity):
-            msg = entity.req._(
-                'You can either submit a new file using the browse button above'
-                ', or choose to remove already uploaded file by checking the '
-                '"detach attached file" check-box, or edit file content online '
-                'with the widget below.')
-        else:
-            msg = entity.req._(
-                'You can either submit a new file using the browse button above'
-                ', or edit file content online with the widget below.')
-        return msg
-
-    def _edit_render(self, entity):
-        wdgs = [self._file_wdg(entity)]
-        if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'):
-            msg = self._edit_msg(entity)
-            wdgs.append(u'<p><b>%s</b></p>' % msg)
-            twdg = TextWidget(self.vreg, self.subjtype, self.rschema, self.objtype)
-            twdg.rname = self.rname
-            data = getattr(entity, self.name)
-            if data:
-                encoding = entity.attr_metadata(self.name, 'encoding')
-                try:
-                    entity[self.name] = unicode(data.getvalue(), encoding)
-                except UnicodeError:
-                    pass
-                else:
-                    wdgs.append(twdg.edit_render(entity, with_format=False))
-                    entity[self.name] = data # restore Binary value
-            wdgs.append(u'<br/>')
-        return '\n'.join(wdgs)
-
-
-class ComboBoxWidget(Widget):
-    html_attributes = Widget.html_attributes | set(('multiple', 'size'))
-
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 multiple=False, **kwattrs):
-        super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
-                                             **kwattrs)
-        if multiple:
-            self.attrs['multiple'] = 'multiple'
-            if not 'size' in self.attrs:
-                self.attrs['size'] = '5'
-        # disable access key (dunno why but this is not allowed by xhtml 1.0)
-        del self.attrs['accesskey']
-
-    def vocabulary(self, entity):
-        raise NotImplementedError()
-
-    def form_value(self, entity, value, values):
-        if value in values:
-            flag = 'selected="selected"'
-        else:
-            flag = ''
-        return value, flag
-
-    def _edit_render(self, entity):
-        values = self.current_values(entity)
-        if values:
-            res = [self.hidden_input(entity, v) for v in values]
-        else:
-            res = [self.hidden_input(entity, INTERNAL_FIELD_VALUE)]
-        dvalues = self.current_display_values(entity)
-        res.append(u'<select name="%s" %s>' % (self.rname, self.format_attrs()))
-        for label, value in self.vocabulary(entity):
-            if value is None:
-                # handle separator
-                res.append(u'<optgroup label="%s"/>' % (label or ''))
-            else:
-                value, flag = self.form_value(entity, value, dvalues)
-                res.append(u'<option value="%s" %s>%s</option>' % (value, flag, xml_escape(label)))
-        res.append(u'</select>')
-        return '\n'.join(res)
-
-
-class StaticComboBoxWidget(ComboBoxWidget):
-
-    def __init__(self, vreg, subjschema, rschema, objschema,
-                 vocabfunc, multiple=False, sort=False, **kwattrs):
-        super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
-                                                   multiple, **kwattrs)
-        self.sort = sort
-        self.vocabfunc = vocabfunc
-
-    def vocabulary(self, entity):
-        choices = self.vocabfunc(entity=entity)
-        if self.sort:
-            choices = sorted(choices)
-        if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
-            return zip((entity.req._(v) for v in choices), choices)
-        return zip(choices, choices)
-
-
-class EntityLinkComboBoxWidget(ComboBoxWidget):
-    """to be used be specific forms"""
-
-    def current_values(self, entity):
-        if entity.has_eid():
-            return [r[0] for r in entity.related(self.name, self.role)]
-        defaultmeth = 'default_%s_%s' % (self.role, self.name)
-        if hasattr(entity, defaultmeth):
-            return getattr(entity, defaultmeth)()
-        return ()
-
-    def vocabulary(self, entity):
-        return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role)
-
-
-class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget):
-
-    def vocabulary(self, entity, limit=None):
-        req = entity.req
-        # first see if its specified by __linkto form parameters
-        linkedto = entity.linked_to(self.name, self.role)
-        if linkedto:
-            entities = (req.entity_from_eid(eid) for eid in linkedto)
-            return [(entity.view('combobox'), entity.eid) for entity in entities]
-        # it isn't, check if the entity provides a method to get correct values
-        if not self.required(entity):
-            res = [('', INTERNAL_FIELD_VALUE)]
-        else:
-            res = []
-        # vocabulary doesn't include current values, add them
-        if entity.has_eid():
-            rset = entity.related(self.name, self.role)
-            relatedvocab = [(e.view('combobox'), e.eid) for e in rset.entities()]
-        else:
-            relatedvocab = []
-        return res + entity.vocabulary(self.rschema, self.role) + relatedvocab
-
-
-class DynamicComboBoxWidget(RawDynamicComboBoxWidget):
-
-    def vocabulary(self, entity, limit=None):
-        return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit))
-
-
-class AddComboBoxWidget(DynamicComboBoxWidget):
-    def _edit_render(self, entity):
-        req = entity.req
-        req.add_js( ('cubicweb.ajax.js', 'jquery.js', 'cubicweb.widgets.js') )
-        values = self.current_values(entity)
-        if values:
-            res = [self.hidden_input(entity, v) for v in values]
-        else:
-            res = [self.hidden_input(entity, INTERNAL_FIELD_VALUE)]
-        dvalues = self.current_display_values(entity)
-        etype_from = entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0]
-        res.append(u'<select class="widget" cubicweb:etype_to="%s" cubicweb:etype_from="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="AddComboBox" name="%s" %s>'
-                   % (entity.e_schema, etype_from, self.rname, self.format_attrs()))
-        for label, value in self.vocabulary(entity):
-            if value is None:
-                # handle separator
-                res.append(u'<optgroup label="%s"/>' % (label or ''))
-            else:
-                value, flag = self.form_value(entity, value, dvalues)
-                res.append(u'<option value="%s" %s>%s</option>' % (value, flag, xml_escape(label)))
-        res.append(u'</select>')
-        res.append(u'<div id="newvalue">')
-        res.append(u'<input type="text" id="newopt" />')
-        res.append(u'<a href="javascript:noop()" id="add_newopt">&#160;</a></div>')
-        return '\n'.join(res)
-
-
-class IntegerWidget(StringWidget):
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 15
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        return '23'
-
-
-class FloatWidget(StringWidget):
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 15
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        formatstr = req.property_value('ui.float-format')
-        return formatstr % 1.23
-
-    def current_values(self, entity):
-        values = entity.attribute_values(self.name)
-        if values:
-            formatstr = entity.req.property_value('ui.float-format')
-            value = values[0]
-            if value is not None:
-                value = float(value)
-            else:
-                return ()
-            return [formatstr % value]
-        return ()
-
-
-class DecimalWidget(StringWidget):
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 15
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        return '345.0300'
-
-
-class DateWidget(StringWidget):
-    format_key = 'ui.date-format'
-    monthnames = ('january', 'february', 'march', 'april',
-                  'may', 'june', 'july', 'august',
-                  'september', 'october', 'november', 'december')
-    daynames = ('monday', 'tuesday', 'wednesday', 'thursday',
-                'friday', 'saturday', 'sunday')
-
-    @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 __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs.setdefault('size', 10)
-        kwattrs.setdefault('maxlength', 10)
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def current_values(self, entity):
-        values = entity.attribute_values(self.name)
-        if values and hasattr(values[0], 'strftime'):
-            formatstr = entity.req.property_value(self.format_key)
-            return [values[0].strftime(str(formatstr))]
-        return values
-
-    def render_example(self, req):
-        formatstr = req.property_value(self.format_key)
-        return datetime.now().strftime(str(formatstr))
-
-
-    def _edit_render(self, entity):
-        wdg = super(DateWidget, self)._edit_render(entity)
-        cal_button = self.render_calendar_popup(entity)
-        return wdg+cal_button
-
-    def render_help(self, entity):
-        """calendar popup widget"""
-        req = entity.req
-        help = [ u'<div class="helper">' ]
-        descr = self.rschema.rproperty(self.subjtype, self.objtype, 'description')
-        if descr:
-            help.append('<span>%s</span>' % req._(descr))
-        example = self.render_example(req)
-        if example:
-            help.append('<span>(%s: %s)</span>'
-                        % (req._('sample format'), example))
-        help.append(u'</div>')
-        return u'&#160;'.join(help)
-
-    def render_calendar_popup(self, entity):
-        """calendar popup widget"""
-        req = entity.req
-        self.add_localized_infos(req)
-        req.add_js(('cubicweb.ajax.js', 'cubicweb.calendar.js',))
-        req.add_css(('cubicweb.calendar_popup.css',))
-        inputid = self.attrs.get('id', self.rname)
-        helperid = "%shelper" % inputid
-        _today = datetime.now()
-        year = int(req.form.get('year', _today.year))
-        month = int(req.form.get('month', _today.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 DateTimeWidget(DateWidget):
-    format_key = 'ui.datetime-format'
-
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 16
-        kwattrs['maxlength'] = 16
-        DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-    def render_example(self, req):
-        formatstr1 = req.property_value('ui.datetime-format')
-        formatstr2 = req.property_value('ui.date-format')
-        return req._('%(fmt1)s, or without time: %(fmt2)s') % {
-            'fmt1': datetime.now().strftime(str(formatstr1)),
-            'fmt2': datetime.now().strftime(str(formatstr2)),
-            }
-
-
-class TimeWidget(StringWidget):
-    format_key = 'ui.time-format'
-    def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
-        kwattrs['size'] = 5
-        kwattrs['maxlength'] = 5
-        StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
-
-
-class EmailWidget(StringWidget):
-
-    def render(self, entity):
-        email = getattr(entity, self.name)
-        if not email:
-            return u''
-        return u'<a href="mailto:%s">%s</a>' % (email, email)
-
-class URLWidget(StringWidget):
-
-    def render(self, entity):
-        url = getattr(entity, self.name)
-        if not url:
-            return u''
-        url = xml_escape(url)
-        return u'<a href="%s">%s</a>' % (url, url)
-
-class EmbededURLWidget(StringWidget):
-
-    def render(self, entity):
-        url = getattr(entity, self.name)
-        if not url:
-            return u''
-        aurl = xml_escape(entity.build_url('embed', url=url))
-        return u'<a href="%s">%s</a>' % (aurl, url)
-
-
-
-def widget_factory(vreg, subjschema, rschema, objschema, role='subject',
-                   **kwargs):
-    """return the most adapated widget to edit the relation
-    'subjschema rschema objschema' according to information found in the schema
-    """
-    if role == 'subject':
-        eclass, subjschema = _eclass_eschema(subjschema)
-    else:
-        eclass, objschema = _eclass_eschema(objschema)
-    if eclass is not None and rschema in getattr(eclass, 'widgets', ()):
-        wcls = WIDGETS[eclass.widgets[rschema]]
-    elif not rschema.final:
-        card = rschema.rproperty(subjschema, objschema, 'cardinality')
-        if role == 'object':
-            multiple = card[1] in '+*'
-        else: #if role == 'subject':
-            multiple = card[0] in '+*'
-        return DynamicComboBoxWidget(vreg, subjschema, rschema, objschema,
-                                     role=role, multiple=multiple)
-    else:
-        wcls = None
-    factory = FACTORIES.get(objschema, _default_widget_factory)
-    return factory(vreg, subjschema, rschema, objschema, wcls=wcls,
-                   role=role, **kwargs)
-
-
-# factories to find the most adapated widget according to a type and other constraints
-
-def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
-    w = None
-    for c in rschema.rproperty(subjschema, objschema, 'constraints'):
-        if isinstance(c, StaticVocabularyConstraint):
-            # may have been set by a previous SizeConstraint but doesn't make sense
-            # here (even doesn't have the same meaning on a combobox actually)
-            kwargs.pop('size', None)
-            return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema,
-                                                  vocabfunc=c.vocabulary, **kwargs)
-        if isinstance(c, SizeConstraint) and c.max is not None:
-            # don't return here since a StaticVocabularyConstraint may
-            # follow
-            if wcls is None:
-                if c.max < 257:
-                    _wcls = StringWidget
-                else:
-                    _wcls = TextWidget
-            else:
-                _wcls = wcls
-            _wcls.size_constraint_attrs(kwargs, c.max)
-            w = _wcls(vreg, subjschema, rschema, objschema, **kwargs)
-    if w is None:
-        w = (wcls or TextWidget)(vreg, subjschema, rschema, objschema, **kwargs)
-    return w
-
-def _default_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
-    if wcls is None:
-        wcls = _WFACTORIES[objschema]
-    return wcls(vreg, subjschema, rschema, objschema, **kwargs)
-
-FACTORIES = {
-    'String' :  _string_widget_factory,
-    'Boolean':  _default_widget_factory,
-    'Bytes':    _default_widget_factory,
-    'Date':     _default_widget_factory,
-    'Datetime': _default_widget_factory,
-    'Float':    _default_widget_factory,
-    'Decimal':    _default_widget_factory,
-    'Int':      _default_widget_factory,
-    'Password': _default_widget_factory,
-    'Time':     _default_widget_factory,
-    }
-
-# default widget by entity's type
-_WFACTORIES = {
-    'Boolean':  YesNoRadioWidget,
-    'Bytes':    FileWidget,
-    'Date':     DateWidget,
-    'Datetime': DateTimeWidget,
-    'Int':      IntegerWidget,
-    'Float':    FloatWidget,
-    'Decimal':  DecimalWidget,
-    'Password': PasswordWidget,
-    'String' :  StringWidget,
-    'Time':     TimeWidget,
-    }
-
-# widgets registry
-WIDGETS = {}
-def register(widget_list):
-    for obj in widget_list:
-        if isinstance(obj, type) and issubclass(obj, Widget):
-            if obj is Widget or obj is ComboBoxWidget:
-                continue
-            WIDGETS[obj.__name__] = obj
-
-register(globals().values())