web/formwidgets.py
brancholdstable
changeset 8462 a14b6562082b
parent 8193 0c64d6b75303
child 8471 05992aa3fc0d
equal deleted inserted replaced
8231:1bb43e31032d 8462:a14b6562082b
    73 Other widgets
    73 Other widgets
    74 '''''''''''''
    74 '''''''''''''
    75 
    75 
    76 .. autoclass:: cubicweb.web.formwidgets.PasswordInput
    76 .. autoclass:: cubicweb.web.formwidgets.PasswordInput
    77 .. autoclass:: cubicweb.web.formwidgets.IntervalWidget
    77 .. autoclass:: cubicweb.web.formwidgets.IntervalWidget
       
    78 .. autoclass:: cubicweb.web.formwidgets.BitSelect
    78 .. autoclass:: cubicweb.web.formwidgets.HorizontalLayoutWidget
    79 .. autoclass:: cubicweb.web.formwidgets.HorizontalLayoutWidget
    79 .. autoclass:: cubicweb.web.formwidgets.EditableURLWidget
    80 .. autoclass:: cubicweb.web.formwidgets.EditableURLWidget
    80 
    81 
    81 
    82 
    82 Form controls
    83 Form controls
   283 
   284 
   284     # XXX deprecates
   285     # XXX deprecates
   285     def values_and_attributes(self, form, field):
   286     def values_and_attributes(self, form, field):
   286         return self.values(form, field), self.attributes(form, field)
   287         return self.values(form, field), self.attributes(form, field)
   287 
   288 
   288     @deprecated('[3.6] use values_and_attributes')
       
   289     def _render_attrs(self, form, field):
       
   290         """return html tag name, attributes and a list of values for the field
       
   291         """
       
   292         values, attrs = self.values_and_attributes(form, field)
       
   293         return field.input_name(form, self.suffix), values, attrs
       
   294 
       
   295 
   289 
   296 class Input(FieldWidget):
   290 class Input(FieldWidget):
   297     """abstract widget class for <input> tag based widgets"""
   291     """abstract widget class for <input> tag based widgets"""
   298     type = None
   292     type = None
   299 
   293 
   433 class Select(FieldWidget):
   427 class Select(FieldWidget):
   434     """Simple <select>, for field having a specific vocabulary. Will return
   428     """Simple <select>, for field having a specific vocabulary. Will return
   435     an unicode string, or a list of unicode strings.
   429     an unicode string, or a list of unicode strings.
   436     """
   430     """
   437     vocabulary_widget = True
   431     vocabulary_widget = True
   438     default_size = 5
   432     default_size = 10
   439 
   433 
   440     def __init__(self, attrs=None, multiple=False, **kwargs):
   434     def __init__(self, attrs=None, multiple=False, **kwargs):
   441         super(Select, self).__init__(attrs, **kwargs)
   435         super(Select, self).__init__(attrs, **kwargs)
   442         self._multiple = multiple
   436         self._multiple = multiple
   443 
   437 
   457                 if optgroup_opened:
   451                 if optgroup_opened:
   458                     options.append(u'</optgroup>')
   452                     options.append(u'</optgroup>')
   459                 oattrs.setdefault('label', label or '')
   453                 oattrs.setdefault('label', label or '')
   460                 options.append(u'<optgroup %s>' % uilib.sgml_attributes(oattrs))
   454                 options.append(u'<optgroup %s>' % uilib.sgml_attributes(oattrs))
   461                 optgroup_opened = True
   455                 optgroup_opened = True
   462             elif value in curvalues:
   456             elif self.value_selected(value, curvalues):
   463                 options.append(tags.option(label, value=value,
   457                 options.append(tags.option(label, value=value,
   464                                            selected='selected', **oattrs))
   458                                            selected='selected', **oattrs))
   465             else:
   459             else:
   466                 options.append(tags.option(label, value=value, **oattrs))
   460                 options.append(tags.option(label, value=value, **oattrs))
   467         if optgroup_opened:
   461         if optgroup_opened:
   472             else:
   466             else:
   473                 size = u'1'
   467                 size = u'1'
   474             attrs['size'] = size
   468             attrs['size'] = size
   475         return tags.select(name=field.input_name(form, self.suffix),
   469         return tags.select(name=field.input_name(form, self.suffix),
   476                            multiple=self._multiple, options=options, **attrs)
   470                            multiple=self._multiple, options=options, **attrs)
       
   471 
       
   472     def value_selected(self, value, curvalues):
       
   473         return value in curvalues
       
   474 
       
   475 
       
   476 class InOutWidget(Select):
       
   477     needs_js = ('cubicweb.widgets.js', )
       
   478     default_size = 10
       
   479     template = """
       
   480 <table id="%(widgetid)s">
       
   481   <tr>
       
   482     <td>%(inoutinput)s</td>
       
   483     <td><div style="margin-bottom:3px">%(addinput)s</div>
       
   484         <div>%(removeinput)s</div>
       
   485     </td>
       
   486     <td>%(resinput)s</td>
       
   487   </tr>
       
   488 </table>
       
   489 """
       
   490     add_button = ('<input type="button" id="cwinoutadd" class="wdgButton cwinoutadd" '
       
   491                   'value="&gt;&gt;" size="10" />')
       
   492     remove_button = ('<input type="button" class="wdgButton cwinoutremove" '
       
   493                      'value="&lt;&lt;" size="10" />')
       
   494 
       
   495     def __init__(self, *args, **kwargs):
       
   496         super(InOutWidget, self).__init__(*args, **kwargs)
       
   497         self._multiple = True
       
   498 
       
   499     def render_select(self, form, field, name, selected=False):
       
   500         values, attrs = self.values_and_attributes(form, field)
       
   501         options = []
       
   502         inputs = []
       
   503         for option in field.vocabulary(form):
       
   504             try:
       
   505                 label, value, _oattrs = option
       
   506             except ValueError:
       
   507                 label, value = option
       
   508             if selected:
       
   509                 # add values
       
   510                 if value in values:
       
   511                     options.append(tags.option(label, value=value))
       
   512                     # add hidden inputs
       
   513                     inputs.append(tags.input(value=value,
       
   514                                              name=field.dom_id(form),
       
   515                                              type="hidden"))
       
   516             else:
       
   517                 options.append(tags.option(label, value=value))
       
   518         if 'size' not in attrs:
       
   519             attrs['size'] = self.default_size
       
   520         if 'id' in attrs :
       
   521             attrs.pop('id')
       
   522         return tags.select(name=name, multiple=self._multiple, id=name,
       
   523                            options=options, **attrs) + '\n'.join(inputs)
       
   524 
       
   525 
       
   526     def _render(self, form, field, renderer):
       
   527         domid = field.dom_id(form)
       
   528         jsnodes = {'widgetid': domid,
       
   529                    'from': 'from_' + domid,
       
   530                    'to': 'to_' + domid}
       
   531         form._cw.add_onload(u'$(cw.jqNode("%s")).cwinoutwidget("%s", "%s");'
       
   532                             % (jsnodes['widgetid'], jsnodes['from'], jsnodes['to']))
       
   533         field.required = True
       
   534         return (self.template %
       
   535                 {'widgetid': jsnodes['widgetid'],
       
   536                  # helpinfo select tag
       
   537                  'inoutinput' : self.render_select(form, field, jsnodes['from']),
       
   538                  # select tag with resultats
       
   539                  'resinput' : self.render_select(form, field, jsnodes['to'], selected=True),
       
   540                  'addinput' : self.add_button % jsnodes,
       
   541                  'removeinput': self.remove_button % jsnodes
       
   542                  })
       
   543 
       
   544 class BitSelect(Select):
       
   545     """Select widget for IntField using a vocabulary with bit masks as values.
       
   546 
       
   547     See also :class:`~cubicweb.web.facet.BitFieldFacet`.
       
   548     """
       
   549     def __init__(self, attrs=None, multiple=True, **kwargs):
       
   550         super(BitSelect, self).__init__(attrs, multiple=multiple, **kwargs)
       
   551 
       
   552     def value_selected(self, value, curvalues):
       
   553         mask = reduce(lambda x, y: int(x) | int(y), curvalues, 0)
       
   554         return int(value) & mask
       
   555 
       
   556     def process_field_data(self, form, field):
       
   557         """Return process posted value(s) for widget and return something
       
   558         understandable by the associated `field`. That value may be correctly
       
   559         typed or a string that the field may parse.
       
   560         """
       
   561         val = super(BitSelect, self).process_field_data(form, field)
       
   562         if isinstance(val, list):
       
   563             val = reduce(lambda x, y: int(x) | int(y), val, 0)
       
   564         elif val:
       
   565             val = int(val)
       
   566         else:
       
   567             val = 0
       
   568         return val
   477 
   569 
   478 
   570 
   479 class CheckBox(Input):
   571 class CheckBox(Input):
   480     """Simple <input type='checkbox'>, for field having a specific
   572     """Simple <input type='checkbox'>, for field having a specific
   481     vocabulary. One input will be generated for each possible value.
   573     vocabulary. One input will be generated for each possible value.
   732     default_settings = {}
   824     default_settings = {}
   733 
   825 
   734     def __init__(self, *args, **kwargs):
   826     def __init__(self, *args, **kwargs):
   735         self.autocomplete_settings = kwargs.pop('autocomplete_settings',
   827         self.autocomplete_settings = kwargs.pop('autocomplete_settings',
   736                                                 self.default_settings)
   828                                                 self.default_settings)
   737         try:
   829         self.autocomplete_initfunc = kwargs.pop('autocomplete_initfunc')
   738             self.autocomplete_initfunc = kwargs.pop('autocomplete_initfunc')
       
   739         except KeyError:
       
   740             warn('[3.6] use autocomplete_initfunc argument of %s constructor '
       
   741                  'instead of relying on autocomplete_initfuncs dictionary on '
       
   742                  'the entity class' % self.__class__.__name__,
       
   743                  DeprecationWarning)
       
   744             self.autocomplete_initfunc = None
       
   745         super(AutoCompletionWidget, self).__init__(*args, **kwargs)
   830         super(AutoCompletionWidget, self).__init__(*args, **kwargs)
   746 
   831 
   747     def values(self, form, field):
   832     def values(self, form, field):
   748         values = super(AutoCompletionWidget, self).values(form, field)
   833         values = super(AutoCompletionWidget, self).values(form, field)
   749         if not values:
   834         if not values:
   761                             % (domid, json_dumps(data),
   846                             % (domid, json_dumps(data),
   762                                json_dumps(self.autocomplete_settings)))
   847                                json_dumps(self.autocomplete_settings)))
   763         return super(AutoCompletionWidget, self)._render(form, field, renderer)
   848         return super(AutoCompletionWidget, self)._render(form, field, renderer)
   764 
   849 
   765     def _get_url(self, entity, field):
   850     def _get_url(self, entity, field):
   766         if self.autocomplete_initfunc is None:
   851         fname = self.autocomplete_initfunc
   767             # XXX for bw compat
       
   768             fname = entity.autocomplete_initfuncs[field.name]
       
   769         else:
       
   770             fname = self.autocomplete_initfunc
       
   771         return entity._cw.build_url('json', fname=fname, mode='remote',
   852         return entity._cw.build_url('json', fname=fname, mode='remote',
   772                                     pageid=entity._cw.pageid)
   853                                     pageid=entity._cw.pageid)
   773 
   854 
   774 
   855 
   775 
   856 
   776 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
   857 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
   777     """XXX describe me"""
   858     """XXX describe me"""
   778     wdgtype = 'StaticFileSuggestField'
   859     wdgtype = 'StaticFileSuggestField'
   779 
   860 
   780     def _get_url(self, entity, field):
   861     def _get_url(self, entity, field):
   781         if self.autocomplete_initfunc is None:
   862         return entity._cw.data_url(self.autocomplete_initfunc)
   782             # XXX for bw compat
       
   783             fname = entity.autocomplete_initfuncs[field.name]
       
   784         else:
       
   785             fname = self.autocomplete_initfunc
       
   786         return entity._cw.data_url(fname)
       
   787 
   863 
   788 
   864 
   789 class RestrictedAutoCompletionWidget(AutoCompletionWidget):
   865 class RestrictedAutoCompletionWidget(AutoCompletionWidget):
   790     """XXX describe me"""
   866     """XXX describe me"""
   791     default_settings = {'mustMatch': True}
   867     default_settings = {'mustMatch': True}
  1013         return '<a id="%(domid)s" href="%(href)s">'\
  1089         return '<a id="%(domid)s" href="%(href)s">'\
  1014                '<img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % {
  1090                '<img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % {
  1015             'label': label, 'imgsrc': imgsrc,
  1091             'label': label, 'imgsrc': imgsrc,
  1016             'domid': self.domid, 'href': self.href}
  1092             'domid': self.domid, 'href': self.href}
  1017 
  1093 
  1018 class InOutWidget(Select):
       
  1019     needs_js = ('cubicweb.widgets.js', )
       
  1020     template = """
       
  1021 <table id="%(widgetid)s">
       
  1022 <tr><td>%(inoutinput)s</td>
       
  1023     <td><div style="margin-bottom:3px">%(addinput)s</div> <div>%(removeinput)s</div></td>
       
  1024     <td>%(resinput)s</td></tr>
       
  1025 </table>
       
  1026 """
       
  1027     add_button = """<input type="button" id="cwinoutadd"  class="wdgButton cwinoutadd" value="&gt;&gt;" size="10" />"""
       
  1028     remove_button ="""<input type="button" class="wdgButton cwinoutremove" value="&lt;&lt;" size="10" />"""
       
  1029 
       
  1030     def __init__(self, attrs=None):
       
  1031         super(InOutWidget, self).__init__(attrs, multiple=True)
       
  1032 
       
  1033     def render_select(self, form, field, name, selected=False):
       
  1034         values, attrs = self.values_and_attributes(form, field)
       
  1035         options = []
       
  1036         inputs = []
       
  1037         for _option in field.vocabulary(form):
       
  1038             try:
       
  1039                 label, value, oattrs = _option
       
  1040             except ValueError:
       
  1041                 label, value = _option
       
  1042             if selected:
       
  1043                 # add values
       
  1044                 if value in values:
       
  1045                     options.append(tags.option(label, value=value))
       
  1046                     # add hidden inputs
       
  1047                     inputs.append(tags.input(value=value, name=field.dom_id(form), type="hidden"))
       
  1048             else:
       
  1049                 options.append(tags.option(label, value=value))
       
  1050         if 'size' not in attrs:
       
  1051             attrs['size'] = 5
       
  1052         if 'id' in attrs :
       
  1053             attrs.pop('id')
       
  1054         return tags.select(name=name, multiple=self._multiple, id=name,
       
  1055                            options=options, **attrs) + '\n'.join(inputs)
       
  1056 
       
  1057 
       
  1058     def _render(self, form, field, renderer):
       
  1059         domid = field.dom_id(form)
       
  1060         jsnodes = {'widgetid': domid, 'from': 'from_' + domid, 'to': 'to_' + domid}
       
  1061         form._cw.add_onload(u'$(cw.jqNode("%s")).cwinoutwidget("%s", "%s");'
       
  1062                             % (jsnodes['widgetid'], jsnodes['from'], jsnodes['to']))
       
  1063         field.required=True
       
  1064         return self.template % {'widgetid': jsnodes['widgetid'],
       
  1065                                 'inoutinput' : self.render_select(form, field, jsnodes['from']), # helpinfo select tag
       
  1066                                 'resinput' : self.render_select(form, field, jsnodes['to'], selected=True), # select tag with resultats
       
  1067                                 'addinput' : self.add_button % jsnodes,
       
  1068                                 'removeinput': self.remove_button % jsnodes
       
  1069                                 }