web/widgets.py
branchtls-sprint
changeset 1554 3a3263df6cdd
parent 1360 13ae1121835e
child 1641 2c80b09d8d86
equal deleted inserted replaced
1553:3f91ef2397d0 1554:3a3263df6cdd
    61     need_multipart = False
    61     need_multipart = False
    62     # generate the "id" attribute with the same value as the "name" (html) attribute
    62     # generate the "id" attribute with the same value as the "name" (html) attribute
    63     autoid = True
    63     autoid = True
    64     html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress'))
    64     html_attributes = set(('id', 'class', 'tabindex', 'accesskey', 'onchange', 'onkeypress'))
    65     cubicwebns_attributes = set()
    65     cubicwebns_attributes = set()
    66     
    66 
    67     def __init__(self, vreg, subjschema, rschema, objschema,
    67     def __init__(self, vreg, subjschema, rschema, objschema,
    68                  role='subject', description=None,
    68                  role='subject', description=None,
    69                  **kwattrs):
    69                  **kwattrs):
    70         self.vreg = vreg
    70         self.vreg = vreg
    71         self.rschema = rschema
    71         self.rschema = rschema
    81     def copy(self):
    81     def copy(self):
    82         """shallow copy (useful when you need to modify self.attrs
    82         """shallow copy (useful when you need to modify self.attrs
    83         because widget instances are cached)
    83         because widget instances are cached)
    84         """
    84         """
    85         # brute force copy (subclasses don't have the
    85         # brute force copy (subclasses don't have the
    86         # same __init__ prototype) 
    86         # same __init__ prototype)
    87         widget = self.__new__(self.__class__)
    87         widget = self.__new__(self.__class__)
    88         widget.__dict__ = dict(self.__dict__)
    88         widget.__dict__ = dict(self.__dict__)
    89         widget.attrs = dict(widget.attrs)
    89         widget.attrs = dict(widget.attrs)
    90         return widget
    90         return widget
    91     
    91 
    92     @staticmethod
    92     @staticmethod
    93     def size_constraint_attrs(attrs, maxsize):
    93     def size_constraint_attrs(attrs, maxsize):
    94         """set html attributes in the attrs dict to consider maxsize"""
    94         """set html attributes in the attrs dict to consider maxsize"""
    95         pass
    95         pass
    96 
    96 
   103             if name in self.cubicwebns_attributes:
   103             if name in self.cubicwebns_attributes:
   104                 attrs.append(u'cubicweb:%s="%s"' % (name, value))
   104                 attrs.append(u'cubicweb:%s="%s"' % (name, value))
   105             elif name in self.html_attributes:
   105             elif name in self.html_attributes:
   106                 attrs.append(u'%s="%s"' % (name, value))
   106                 attrs.append(u'%s="%s"' % (name, value))
   107         return u' '.join(sorted(attrs))
   107         return u' '.join(sorted(attrs))
   108         
   108 
   109     def required(self, entity):
   109     def required(self, entity):
   110         """indicates if the widget needs a value to be filled in"""
   110         """indicates if the widget needs a value to be filled in"""
   111         card = self.rschema.cardinality(self.subjtype, self.objtype, self.role)
   111         card = self.rschema.cardinality(self.subjtype, self.objtype, self.role)
   112         return card in '1+'
   112         return card in '1+'
   113 
   113 
   114     def input_id(self, entity):
   114     def input_id(self, entity):
   115         try:
   115         try:
   116             return self.rname
   116             return self.rname
   117         except AttributeError:
   117         except AttributeError:
   118             return eid_param(self.name, entity.eid)
   118             return eid_param(self.name, entity.eid)
   119     
   119 
   120     def render_label(self, entity, label=None):
   120     def render_label(self, entity, label=None):
   121         """render widget's label"""
   121         """render widget's label"""
   122         label = label or self.rschema.display_name(entity.req, self.role)
   122         label = label or self.rschema.display_name(entity.req, self.role)
   123         forid = self.input_id(entity)
   123         forid = self.input_id(entity)
   124         if forid:
   124         if forid:
   128         if self.required(entity):
   128         if self.required(entity):
   129             label = u'<label class="required"%s>%s</label>' % (forattr, label)
   129             label = u'<label class="required"%s>%s</label>' % (forattr, label)
   130         else:
   130         else:
   131             label = u'<label%s>%s</label>' % (forattr, label)
   131             label = u'<label%s>%s</label>' % (forattr, label)
   132         return label
   132         return label
   133     
   133 
   134     def render_error(self, entity):
   134     def render_error(self, entity):
   135         """return validation error for widget's field of the given entity, if
   135         """return validation error for widget's field of the given entity, if
   136         any
   136         any
   137         """
   137         """
   138         errex = entity.req.data.get('formerrors')
   138         errex = entity.req.data.get('formerrors')
   151         example = self.render_example(req)
   151         example = self.render_example(req)
   152         if example:
   152         if example:
   153             help.append(u'<span class="helper">(%s: %s)</span>'
   153             help.append(u'<span class="helper">(%s: %s)</span>'
   154                         % (req._('sample format'), example))
   154                         % (req._('sample format'), example))
   155         return u'&nbsp;'.join(help)
   155         return u'&nbsp;'.join(help)
   156     
   156 
   157     def render_example(self, req):
   157     def render_example(self, req):
   158         return u''
   158         return u''
   159         
   159 
   160     def render(self, entity):
   160     def render(self, entity):
   161         """render the widget for a simple view"""
   161         """render the widget for a simple view"""
   162         if not entity.has_eid():
   162         if not entity.has_eid():
   163             return u''
   163             return u''
   164         return entity.printable_value(self.name)
   164         return entity.printable_value(self.name)
   165     
   165 
   166     def edit_render(self, entity, tabindex=None,
   166     def edit_render(self, entity, tabindex=None,
   167                     includehelp=False, useid=None, **kwargs):
   167                     includehelp=False, useid=None, **kwargs):
   168         """render the widget for edition"""
   168         """render the widget for edition"""
   169         # this is necessary to handle multiple edition
   169         # this is necessary to handle multiple edition
   170         self.rname = eid_param(self.name, entity.eid)
   170         self.rname = eid_param(self.name, entity.eid)
   178             self.attrs['tabindex'] = entity.req.next_tabindex()
   178             self.attrs['tabindex'] = entity.req.next_tabindex()
   179         output = self._edit_render(entity, **kwargs)
   179         output = self._edit_render(entity, **kwargs)
   180         if includehelp:
   180         if includehelp:
   181             output += self.render_help(entity)
   181             output += self.render_help(entity)
   182         return output
   182         return output
   183     
   183 
   184     def _edit_render(self, entity):
   184     def _edit_render(self, entity):
   185         """do the actual job to render the widget for edition"""
   185         """do the actual job to render the widget for edition"""
   186         raise NotImplementedError
   186         raise NotImplementedError
   187 
   187 
   188     def current_values(self, entity):
   188     def current_values(self, entity):
   194         if self.rschema.is_final():
   194         if self.rschema.is_final():
   195             return entity.attribute_values(self.name)
   195             return entity.attribute_values(self.name)
   196         elif entity.has_eid():
   196         elif entity.has_eid():
   197             return [row[0] for row in entity.related(self.name, self.role)]
   197             return [row[0] for row in entity.related(self.name, self.role)]
   198         return ()
   198         return ()
   199             
   199 
   200     def current_value(self, entity):
   200     def current_value(self, entity):
   201         return _value_from_values(self.current_values(entity))
   201         return _value_from_values(self.current_values(entity))
   202 
   202 
   203     def current_display_values(self, entity):
   203     def current_display_values(self, entity):
   204         """same as .current_values but consider values stored in session in case
   204         """same as .current_values but consider values stored in session in case
   211         if cdvalues is None:
   211         if cdvalues is None:
   212             return self.current_values(entity)
   212             return self.current_values(entity)
   213         if not isinstance(cdvalues, (list, tuple)):
   213         if not isinstance(cdvalues, (list, tuple)):
   214             cdvalues = (cdvalues,)
   214             cdvalues = (cdvalues,)
   215         return cdvalues
   215         return cdvalues
   216     
   216 
   217     def current_display_value(self, entity):
   217     def current_display_value(self, entity):
   218         """same as .current_value but consider values stored in session in case
   218         """same as .current_value but consider values stored in session in case
   219         of validation error
   219         of validation error
   220         """
   220         """
   221         return _value_from_values(self.current_display_values(entity))
   221         return _value_from_values(self.current_display_values(entity))
   222     
   222 
   223     def hidden_input(self, entity, qvalue):
   223     def hidden_input(self, entity, qvalue):
   224         """return an hidden field which
   224         """return an hidden field which
   225         1. indicates that a field is edited
   225         1. indicates that a field is edited
   226         2. hold the old value to easily detect if the field has been modified
   226         2. hold the old value to easily detect if the field has been modified
   227 
   227 
   256     input_type = 'hidden'
   256     input_type = 'hidden'
   257     autoid = False
   257     autoid = False
   258     def __init__(self, vreg, subjschema, rschema, objschema,
   258     def __init__(self, vreg, subjschema, rschema, objschema,
   259                  role='subject', **kwattrs):
   259                  role='subject', **kwattrs):
   260         InputWidget.__init__(self, vreg, subjschema, rschema, objschema,
   260         InputWidget.__init__(self, vreg, subjschema, rschema, objschema,
   261                              role='subject', 
   261                              role='subject',
   262                              **kwattrs)
   262                              **kwattrs)
   263         # disable access key
   263         # disable access key
   264         del self.attrs['accesskey']
   264         del self.attrs['accesskey']
   265 
   265 
   266     def current_value(self, entity):
   266     def current_value(self, entity):
   268         return value or INTERNAL_FIELD_VALUE
   268         return value or INTERNAL_FIELD_VALUE
   269 
   269 
   270     def current_display_value(self, entity):
   270     def current_display_value(self, entity):
   271         value = InputWidget.current_display_value(self, entity)
   271         value = InputWidget.current_display_value(self, entity)
   272         return value or INTERNAL_FIELD_VALUE
   272         return value or INTERNAL_FIELD_VALUE
   273     
   273 
   274     def render_label(self, entity, label=None):
   274     def render_label(self, entity, label=None):
   275         """render widget's label"""
   275         """render widget's label"""
   276         return u''
   276         return u''
   277     
   277 
   278     def render_help(self, entity):
   278     def render_help(self, entity):
   279         return u''
   279         return u''
   280     
   280 
   281     def hidden_input(self, entity, value):
   281     def hidden_input(self, entity, value):
   282         """no hidden input for hidden input"""
   282         """no hidden input for hidden input"""
   283         return ''
   283         return ''
   284     
   284 
   285 
   285 
   286 class EidWidget(HiddenWidget):
   286 class EidWidget(HiddenWidget):
   287 
   287 
   288     def _edit_render(self, entity):
   288     def _edit_render(self, entity):
   289         return u'<input type="hidden" name="eid" value="%s" />' % entity.eid
   289         return u'<input type="hidden" name="eid" value="%s" />' % entity.eid
   295     @staticmethod
   295     @staticmethod
   296     def size_constraint_attrs(attrs, maxsize):
   296     def size_constraint_attrs(attrs, maxsize):
   297         """set html attributes in the attrs dict to consider maxsize"""
   297         """set html attributes in the attrs dict to consider maxsize"""
   298         attrs['size'] = min(maxsize, 40)
   298         attrs['size'] = min(maxsize, 40)
   299         attrs['maxlength'] = maxsize
   299         attrs['maxlength'] = maxsize
   300         
   300 
   301         
   301 
   302 class AutoCompletionWidget(StringWidget):
   302 class AutoCompletionWidget(StringWidget):
   303     cubicwebns_attributes = (StringWidget.cubicwebns_attributes |
   303     cubicwebns_attributes = (StringWidget.cubicwebns_attributes |
   304                           set(('accesskey', 'size', 'maxlength')))
   304                           set(('accesskey', 'size', 'maxlength')))
   305     attrs = ()
   305     attrs = ()
   306     
   306 
   307     wdgtype = 'SuggestField'
   307     wdgtype = 'SuggestField'
   308     
   308 
   309     def current_value(self, entity):
   309     def current_value(self, entity):
   310         value = StringWidget.current_value(self, entity)
   310         value = StringWidget.current_value(self, entity)
   311         return value or INTERNAL_FIELD_VALUE
   311         return value or INTERNAL_FIELD_VALUE
   312 
   312 
   313     def _get_url(self, entity):
   313     def _get_url(self, entity):
   342                     'required' : cssclass,
   342                     'required' : cssclass,
   343                     })
   343                     })
   344 
   344 
   345 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
   345 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
   346     wdgtype = 'StaticFileSuggestField'
   346     wdgtype = 'StaticFileSuggestField'
   347     
   347 
   348     def _get_url(self, entity):
   348     def _get_url(self, entity):
   349         return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
   349         return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
   350 
   350 
   351 class RestrictedAutoCompletionWidget(AutoCompletionWidget):
   351 class RestrictedAutoCompletionWidget(AutoCompletionWidget):
   352     wdgtype = 'RestrictedSuggestField'    
   352     wdgtype = 'RestrictedSuggestField'
   353 
   353 
   354     
   354 
   355 class PasswordWidget(InputWidget):
   355 class PasswordWidget(InputWidget):
   356     input_type = 'password'
   356     input_type = 'password'
   357     
   357 
   358     def required(self, entity):
   358     def required(self, entity):
   359         if InputWidget.required(self, entity) and not entity.has_eid():
   359         if InputWidget.required(self, entity) and not entity.has_eid():
   360             return True
   360             return True
   361         return False
   361         return False
   362     
   362 
   363     def current_values(self, entity):
   363     def current_values(self, entity):
   364         # on existant entity, show password field has non empty (we don't have
   364         # on existant entity, show password field has non empty (we don't have
   365         # the actual value
   365         # the actual value
   366         if entity.has_eid():
   366         if entity.has_eid():
   367             return (INTERNAL_FIELD_VALUE,)
   367             return (INTERNAL_FIELD_VALUE,)
   372         name = eid_param(self.name + '-confirm', entity.eid)
   372         name = eid_param(self.name + '-confirm', entity.eid)
   373         return u'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/>&nbsp;<span class="emphasis">(%s)</span>' % (
   373         return u'%s<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/>&nbsp;<span class="emphasis">(%s)</span>' % (
   374             html, self.input_type, name, name, entity.req.next_tabindex(),
   374             html, self.input_type, name, name, entity.req.next_tabindex(),
   375             entity.req._('confirm password'))
   375             entity.req._('confirm password'))
   376 
   376 
   377     
   377 
   378 class TextWidget(Widget):
   378 class TextWidget(Widget):
   379     html_attributes = Widget.html_attributes | set(('rows', 'cols'))
   379     html_attributes = Widget.html_attributes | set(('rows', 'cols'))
   380     
   380 
   381     @staticmethod
   381     @staticmethod
   382     def size_constraint_attrs(attrs, maxsize):
   382     def size_constraint_attrs(attrs, maxsize):
   383         """set html attributes in the attrs dict to consider maxsize"""
   383         """set html attributes in the attrs dict to consider maxsize"""
   384         if 256 < maxsize < 513:
   384         if 256 < maxsize < 513:
   385             attrs['cols'], attrs['rows'] = 60, 5
   385             attrs['cols'], attrs['rows'] = 60, 5
   386         else:
   386         else:
   387             attrs['cols'], attrs['rows'] = 80, 10
   387             attrs['cols'], attrs['rows'] = 80, 10
   388     
   388 
   389     def render(self, entity):
   389     def render(self, entity):
   390         if not entity.has_eid():
   390         if not entity.has_eid():
   391             return u''
   391             return u''
   392         return entity.printable_value(self.name)
   392         return entity.printable_value(self.name)
   393         
   393 
   394     def _edit_render(self, entity, with_format=True):
   394     def _edit_render(self, entity, with_format=True):
   395         req = entity.req
   395         req = entity.req
   396         editor = self._edit_render_textarea(entity, with_format)
   396         editor = self._edit_render_textarea(entity, with_format)
   397         value = self.current_value(entity)
   397         value = self.current_value(entity)
   398         if isinstance(value, basestring):
   398         if isinstance(value, basestring):
   399             value = html_escape(value)
   399             value = html_escape(value)
   400         return u'%s%s' % (self.hidden_input(entity, value), editor)
   400         return u'%s%s' % (self.hidden_input(entity, value), editor)
   401     
   401 
   402     def _edit_render_textarea(self, entity, with_format):
   402     def _edit_render_textarea(self, entity, with_format):
   403         self.attrs.setdefault('cols', 80)
   403         self.attrs.setdefault('cols', 80)
   404         self.attrs.setdefault('rows', 20)
   404         self.attrs.setdefault('rows', 20)
   405         dvalue = self.current_display_value(entity)
   405         dvalue = self.current_display_value(entity)
   406         if isinstance(dvalue, basestring):
   406         if isinstance(dvalue, basestring):
   424             self.attrs['tabindex'] = entity.req.next_tabindex()
   424             self.attrs['tabindex'] = entity.req.next_tabindex()
   425         else:
   425         else:
   426             fmtwdgstr = ''
   426             fmtwdgstr = ''
   427         return u'%s<br/><textarea onkeypress="autogrow(this)" name="%s" %s>%s</textarea>' % (
   427         return u'%s<br/><textarea onkeypress="autogrow(this)" name="%s" %s>%s</textarea>' % (
   428             fmtwdgstr, self.rname, self.format_attrs(), dvalue)
   428             fmtwdgstr, self.rname, self.format_attrs(), dvalue)
   429             
   429 
   430     
   430 
   431 class CheckBoxWidget(Widget):
   431 class CheckBoxWidget(Widget):
   432     html_attributes = Widget.html_attributes | set(('checked', ))
   432     html_attributes = Widget.html_attributes | set(('checked', ))
   433     def _edit_render(self, entity):
   433     def _edit_render(self, entity):
   434         value = self.current_value(entity)
   434         value = self.current_value(entity)
   435         dvalue = self.current_display_value(entity)
   435         dvalue = self.current_display_value(entity)
   458         wdgs = [self.hidden_input(entity, value),
   458         wdgs = [self.hidden_input(entity, value),
   459                 u'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')),
   459                 u'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')),
   460                 u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))]
   460                 u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))]
   461         return '\n'.join(wdgs)
   461         return '\n'.join(wdgs)
   462 
   462 
   463     
   463 
   464 class FileWidget(Widget):
   464 class FileWidget(Widget):
   465     need_multipart = True
   465     need_multipart = True
   466     def _file_wdg(self, entity):
   466     def _file_wdg(self, entity):
   467         wdgs = [u'<input type="file" name="%s" %s/>' % (self.rname, self.format_attrs())]
   467         wdgs = [u'<input type="file" name="%s" %s/>' % (self.rname, self.format_attrs())]
   468         req = entity.req
   468         req = entity.req
   490                 wdgs.append(req._('detach attached file %s' % entity.dc_title()))
   490                 wdgs.append(req._('detach attached file %s' % entity.dc_title()))
   491             else:
   491             else:
   492                 wdgs.append(u'<br/>')
   492                 wdgs.append(u'<br/>')
   493                 wdgs.append(req._('currently attached file: %s' % entity.dc_title()))
   493                 wdgs.append(req._('currently attached file: %s' % entity.dc_title()))
   494         return '\n'.join(wdgs)
   494         return '\n'.join(wdgs)
   495     
   495 
   496     def _edit_render(self, entity):
   496     def _edit_render(self, entity):
   497         return self.hidden_input(entity, None) + self._file_wdg(entity)
   497         return self.hidden_input(entity, None) + self._file_wdg(entity)
   498 
   498 
   499 
   499 
   500 class TextFileWidget(FileWidget):
   500 class TextFileWidget(FileWidget):
   508         else:
   508         else:
   509             msg = entity.req._(
   509             msg = entity.req._(
   510                 'You can either submit a new file using the browse button above'
   510                 'You can either submit a new file using the browse button above'
   511                 ', or edit file content online with the widget below.')
   511                 ', or edit file content online with the widget below.')
   512         return msg
   512         return msg
   513     
   513 
   514     def _edit_render(self, entity):
   514     def _edit_render(self, entity):
   515         wdgs = [self._file_wdg(entity)]
   515         wdgs = [self._file_wdg(entity)]
   516         if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'):
   516         if entity.attr_metadata(self.name, 'format') in ('text/plain', 'text/html', 'text/rest'):
   517             msg = self._edit_msg(entity)
   517             msg = self._edit_msg(entity)
   518             wdgs.append(u'<p><b>%s</b></p>' % msg)
   518             wdgs.append(u'<p><b>%s</b></p>' % msg)
   532         return '\n'.join(wdgs)
   532         return '\n'.join(wdgs)
   533 
   533 
   534 
   534 
   535 class ComboBoxWidget(Widget):
   535 class ComboBoxWidget(Widget):
   536     html_attributes = Widget.html_attributes | set(('multiple', 'size'))
   536     html_attributes = Widget.html_attributes | set(('multiple', 'size'))
   537     
   537 
   538     def __init__(self, vreg, subjschema, rschema, objschema,
   538     def __init__(self, vreg, subjschema, rschema, objschema,
   539                  multiple=False, **kwattrs):
   539                  multiple=False, **kwattrs):
   540         super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
   540         super(ComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
   541                                              **kwattrs)
   541                                              **kwattrs)
   542         if multiple:
   542         if multiple:
   543             self.attrs['multiple'] = 'multiple'
   543             self.attrs['multiple'] = 'multiple'
   544             if not 'size' in self.attrs:
   544             if not 'size' in self.attrs:
   545                 self.attrs['size'] = '5'
   545                 self.attrs['size'] = '5'
   546         # disable access key (dunno why but this is not allowed by xhtml 1.0)
   546         # disable access key (dunno why but this is not allowed by xhtml 1.0)
   547         del self.attrs['accesskey']
   547         del self.attrs['accesskey']
   548         
   548 
   549     def vocabulary(self, entity):
   549     def vocabulary(self, entity):
   550         raise NotImplementedError()
   550         raise NotImplementedError()
   551     
   551 
   552     def form_value(self, entity, value, values):
   552     def form_value(self, entity, value, values):
   553         if value in values:
   553         if value in values:
   554             flag = 'selected="selected"'
   554             flag = 'selected="selected"'
   555         else:
   555         else:
   556             flag = ''
   556             flag = ''
   572                 value, flag = self.form_value(entity, value, dvalues)
   572                 value, flag = self.form_value(entity, value, dvalues)
   573                 res.append(u'<option value="%s" %s>%s</option>' % (value, flag, html_escape(label)))
   573                 res.append(u'<option value="%s" %s>%s</option>' % (value, flag, html_escape(label)))
   574         res.append(u'</select>')
   574         res.append(u'</select>')
   575         return '\n'.join(res)
   575         return '\n'.join(res)
   576 
   576 
   577  
   577 
   578 class StaticComboBoxWidget(ComboBoxWidget):
   578 class StaticComboBoxWidget(ComboBoxWidget):
   579     
   579 
   580     def __init__(self, vreg, subjschema, rschema, objschema,
   580     def __init__(self, vreg, subjschema, rschema, objschema,
   581                  vocabfunc, multiple=False, sort=False, **kwattrs):
   581                  vocabfunc, multiple=False, sort=False, **kwattrs):
   582         super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
   582         super(StaticComboBoxWidget, self).__init__(vreg, subjschema, rschema, objschema,
   583                                                    multiple, **kwattrs)
   583                                                    multiple, **kwattrs)
   584         self.sort = sort
   584         self.sort = sort
   589         if self.sort:
   589         if self.sort:
   590             choices = sorted(choices)
   590             choices = sorted(choices)
   591         if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
   591         if self.rschema.rproperty(self.subjtype, self.objtype, 'internationalizable'):
   592             return zip((entity.req._(v) for v in choices), choices)
   592             return zip((entity.req._(v) for v in choices), choices)
   593         return zip(choices, choices)
   593         return zip(choices, choices)
   594     
   594 
   595 
   595 
   596 class EntityLinkComboBoxWidget(ComboBoxWidget):
   596 class EntityLinkComboBoxWidget(ComboBoxWidget):
   597     """to be used be specific forms"""
   597     """to be used be specific forms"""
   598     
   598 
   599     def current_values(self, entity):
   599     def current_values(self, entity):
   600         if entity.has_eid():
   600         if entity.has_eid():
   601             return [r[0] for r in entity.related(self.name, self.role)]
   601             return [r[0] for r in entity.related(self.name, self.role)]
   602         defaultmeth = 'default_%s_%s' % (self.role, self.name)
   602         defaultmeth = 'default_%s_%s' % (self.role, self.name)
   603         if hasattr(entity, defaultmeth):
   603         if hasattr(entity, defaultmeth):
   604             return getattr(entity, defaultmeth)()
   604             return getattr(entity, defaultmeth)()
   605         return ()
   605         return ()
   606     
   606 
   607     def vocabulary(self, entity):
   607     def vocabulary(self, entity):
   608         return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role)
   608         return [('', INTERNAL_FIELD_VALUE)] + entity.vocabulary(self.rschema, self.role)
   609 
   609 
   610 
   610 
   611 class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget):
   611 class RawDynamicComboBoxWidget(EntityLinkComboBoxWidget):
   612     
   612 
   613     def vocabulary(self, entity, limit=None):
   613     def vocabulary(self, entity, limit=None):
   614         req = entity.req
   614         req = entity.req
   615         # first see if its specified by __linkto form parameters
   615         # first see if its specified by __linkto form parameters
   616         linkedto = entity.linked_to(self.name, self.role)
   616         linkedto = entity.linked_to(self.name, self.role)
   617         if linkedto:
   617         if linkedto:
   630             relatedvocab = []
   630             relatedvocab = []
   631         return res + entity.vocabulary(self.rschema, self.role) + relatedvocab
   631         return res + entity.vocabulary(self.rschema, self.role) + relatedvocab
   632 
   632 
   633 
   633 
   634 class DynamicComboBoxWidget(RawDynamicComboBoxWidget):
   634 class DynamicComboBoxWidget(RawDynamicComboBoxWidget):
   635     
   635 
   636     def vocabulary(self, entity, limit=None):
   636     def vocabulary(self, entity, limit=None):
   637         return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit))
   637         return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit))
   638 
   638 
   639 
   639 
   640 class AddComboBoxWidget(DynamicComboBoxWidget):
   640 class AddComboBoxWidget(DynamicComboBoxWidget):
   667 class IntegerWidget(StringWidget):
   667 class IntegerWidget(StringWidget):
   668     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   668     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   669         kwattrs['size'] = 5
   669         kwattrs['size'] = 5
   670         kwattrs['maxlength'] = 15
   670         kwattrs['maxlength'] = 15
   671         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   671         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   672         
   672 
   673     def render_example(self, req):
   673     def render_example(self, req):
   674         return '23'
   674         return '23'
   675     
   675 
   676         
   676 
   677 class FloatWidget(StringWidget):
   677 class FloatWidget(StringWidget):
   678     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   678     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   679         kwattrs['size'] = 5
   679         kwattrs['size'] = 5
   680         kwattrs['maxlength'] = 15
   680         kwattrs['maxlength'] = 15
   681         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   681         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   682 
   682 
   683     def render_example(self, req):
   683     def render_example(self, req):
   684         formatstr = req.property_value('ui.float-format')
   684         formatstr = req.property_value('ui.float-format')
   685         return formatstr % 1.23
   685         return formatstr % 1.23
   686     
   686 
   687     def current_values(self, entity):
   687     def current_values(self, entity):
   688         values = entity.attribute_values(self.name)
   688         values = entity.attribute_values(self.name)
   689         if values:
   689         if values:
   690             formatstr = entity.req.property_value('ui.float-format')
   690             formatstr = entity.req.property_value('ui.float-format')
   691             value = values[0]
   691             value = values[0]
   700 class DecimalWidget(StringWidget):
   700 class DecimalWidget(StringWidget):
   701     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   701     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   702         kwattrs['size'] = 5
   702         kwattrs['size'] = 5
   703         kwattrs['maxlength'] = 15
   703         kwattrs['maxlength'] = 15
   704         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   704         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   705         
   705 
   706     def render_example(self, req):
   706     def render_example(self, req):
   707         return '345.0300'
   707         return '345.0300'
   708 
   708 
   709 
   709 
   710 class DateWidget(StringWidget):
   710 class DateWidget(StringWidget):
   722         _ = req._
   722         _ = req._
   723         monthnames = [_(mname) for mname in cls.monthnames]
   723         monthnames = [_(mname) for mname in cls.monthnames]
   724         daynames = [_(dname) for dname in cls.daynames]
   724         daynames = [_(dname) for dname in cls.daynames]
   725         req.html_headers.define_var('MONTHNAMES', monthnames)
   725         req.html_headers.define_var('MONTHNAMES', monthnames)
   726         req.html_headers.define_var('DAYNAMES', daynames)
   726         req.html_headers.define_var('DAYNAMES', daynames)
   727     
   727 
   728     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   728     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   729         kwattrs.setdefault('size', 10)
   729         kwattrs.setdefault('size', 10)
   730         kwattrs.setdefault('maxlength', 10)
   730         kwattrs.setdefault('maxlength', 10)
   731         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   731         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   732 
   732 
   782 
   782 
   783     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   783     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   784         kwattrs['size'] = 16
   784         kwattrs['size'] = 16
   785         kwattrs['maxlength'] = 16
   785         kwattrs['maxlength'] = 16
   786         DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   786         DateWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   787     
   787 
   788     def render_example(self, req):
   788     def render_example(self, req):
   789         formatstr1 = req.property_value('ui.datetime-format')
   789         formatstr1 = req.property_value('ui.datetime-format')
   790         formatstr2 = req.property_value('ui.date-format')
   790         formatstr2 = req.property_value('ui.date-format')
   791         return req._('%(fmt1)s, or without time: %(fmt2)s') % {
   791         return req._('%(fmt1)s, or without time: %(fmt2)s') % {
   792             'fmt1': datetime.now().strftime(formatstr1),
   792             'fmt1': datetime.now().strftime(formatstr1),
   799     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   799     def __init__(self, vreg, subjschema, rschema, objschema, **kwattrs):
   800         kwattrs['size'] = 5
   800         kwattrs['size'] = 5
   801         kwattrs['maxlength'] = 5
   801         kwattrs['maxlength'] = 5
   802         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   802         StringWidget.__init__(self, vreg, subjschema, rschema, objschema, **kwattrs)
   803 
   803 
   804         
   804 
   805 class EmailWidget(StringWidget):
   805 class EmailWidget(StringWidget):
   806     
   806 
   807     def render(self, entity):
   807     def render(self, entity):
   808         email = getattr(entity, self.name)
   808         email = getattr(entity, self.name)
   809         if not email:
   809         if not email:
   810             return u''
   810             return u''
   811         return u'<a href="mailto:%s">%s</a>' % (email, email)
   811         return u'<a href="mailto:%s">%s</a>' % (email, email)
   812         
   812 
   813 class URLWidget(StringWidget):
   813 class URLWidget(StringWidget):
   814     
   814 
   815     def render(self, entity):
   815     def render(self, entity):
   816         url = getattr(entity, self.name)
   816         url = getattr(entity, self.name)
   817         if not url:
   817         if not url:
   818             return u''
   818             return u''
   819         url = html_escape(url)
   819         url = html_escape(url)
   820         return u'<a href="%s">%s</a>' % (url, url)
   820         return u'<a href="%s">%s</a>' % (url, url)
   821     
   821 
   822 class EmbededURLWidget(StringWidget):
   822 class EmbededURLWidget(StringWidget):
   823     
   823 
   824     def render(self, entity):
   824     def render(self, entity):
   825         url = getattr(entity, self.name)
   825         url = getattr(entity, self.name)
   826         if not url:
   826         if not url:
   827             return u''
   827             return u''
   828         aurl = html_escape(entity.build_url('embed', url=url))
   828         aurl = html_escape(entity.build_url('embed', url=url))
   829         return u'<a href="%s">%s</a>' % (aurl, url)
   829         return u'<a href="%s">%s</a>' % (aurl, url)
   830 
   830 
   831     
   831 
   832 
   832 
   833 def widget_factory(vreg, subjschema, rschema, objschema, role='subject',
   833 def widget_factory(vreg, subjschema, rschema, objschema, role='subject',
   834                    **kwargs):
   834                    **kwargs):
   835     """return the most adapated widget to edit the relation
   835     """return the most adapated widget to edit the relation
   836     'subjschema rschema objschema' according to information found in the schema
   836     'subjschema rschema objschema' according to information found in the schema
   855     return factory(vreg, subjschema, rschema, objschema, wcls=wcls,
   855     return factory(vreg, subjschema, rschema, objschema, wcls=wcls,
   856                    role=role, **kwargs)
   856                    role=role, **kwargs)
   857 
   857 
   858 
   858 
   859 # factories to find the most adapated widget according to a type and other constraints
   859 # factories to find the most adapated widget according to a type and other constraints
   860                 
   860 
   861 def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
   861 def _string_widget_factory(vreg, subjschema, rschema, objschema, wcls=None, **kwargs):
   862     w = None
   862     w = None
   863     for c in rschema.rproperty(subjschema, objschema, 'constraints'):
   863     for c in rschema.rproperty(subjschema, objschema, 'constraints'):
   864         if isinstance(c, StaticVocabularyConstraint):
   864         if isinstance(c, StaticVocabularyConstraint):
   865             # may have been set by a previous SizeConstraint but doesn't make sense
   865             # may have been set by a previous SizeConstraint but doesn't make sense
   866             # here (even doesn't have the same meaning on a combobox actually)
   866             # here (even doesn't have the same meaning on a combobox actually)
   867             kwargs.pop('size', None) 
   867             kwargs.pop('size', None)
   868             return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema,
   868             return (wcls or StaticComboBoxWidget)(vreg, subjschema, rschema, objschema,
   869                                                   vocabfunc=c.vocabulary, **kwargs)
   869                                                   vocabfunc=c.vocabulary, **kwargs)
   870         if isinstance(c, SizeConstraint) and c.max is not None:
   870         if isinstance(c, SizeConstraint) and c.max is not None:
   871             # don't return here since a StaticVocabularyConstraint may
   871             # don't return here since a StaticVocabularyConstraint may
   872             # follow
   872             # follow
   912     'Decimal':  DecimalWidget,
   912     'Decimal':  DecimalWidget,
   913     'Password': PasswordWidget,
   913     'Password': PasswordWidget,
   914     'String' :  StringWidget,
   914     'String' :  StringWidget,
   915     'Time':     TimeWidget,
   915     'Time':     TimeWidget,
   916     }
   916     }
   917     
   917 
   918 # widgets registry
   918 # widgets registry
   919 WIDGETS = {}
   919 WIDGETS = {}
   920 def register(widget_list):
   920 def register(widget_list):
   921     for obj in widget_list:
   921     for obj in widget_list:
   922         if isinstance(obj, type) and issubclass(obj, Widget):
   922         if isinstance(obj, type) and issubclass(obj, Widget):