merge
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Mon, 04 Jan 2016 17:23:36 +0100
changeset 11050 b356acb36e15
parent 11047 bfd11ffa79f7 (current diff)
parent 11049 1f41697f2e26 (diff)
child 11051 de472896fc0a
merge
--- a/web/data/cubicweb.widgets.js	Fri Dec 18 17:49:45 2015 +0100
+++ b/web/data/cubicweb.widgets.js	Mon Jan 04 17:23:36 2016 +0100
@@ -25,6 +25,23 @@
     return null;
 }
 
+function renderJQueryDatePicker(subject, button_image, date_format, min_date, max_date){
+    $widget = cw.jqNode(subject);
+    $widget.datepicker({buttonImage: button_image, dateFormat: date_format,
+                        firstDay: 1, showOn: "button", buttonImageOnly: true,
+                        minDate: min_date, maxDate: max_date});
+    $widget.change(function(ev) {
+        maxOfId = $(this).data('max-of');
+        if (maxOfId) {
+            cw.jqNode(maxOfId).datepicker("option", "maxDate", this.value);
+        }
+        minOfId = $(this).data('min-of');
+        if (minOfId) {
+            cw.jqNode(minOfId).datepicker("option", "minDate", this.value);
+        }
+    });
+}
+
 /**
  * .. function:: buildWidgets(root)
  *
--- a/web/formwidgets.py	Fri Dec 18 17:49:45 2015 +0100
+++ b/web/formwidgets.py	Mon Jan 04 17:23:36 2016 +0100
@@ -97,12 +97,10 @@
 
 from functools import reduce
 from datetime import date
-from warnings import warn
 
 from six import text_type, string_types
 
 from logilab.mtconverter import xml_escape
-from logilab.common.deprecation import deprecated
 from logilab.common.date import todatetime
 
 from cubicweb import tags, uilib
@@ -210,7 +208,7 @@
         attrs = dict(self.attrs)
         if self.setdomid:
             attrs['id'] = field.dom_id(form, self.suffix)
-        if self.settabindex and not 'tabindex' in attrs:
+        if self.settabindex and 'tabindex' not in attrs:
             attrs['tabindex'] = form._cw.next_tabindex()
         if 'placeholder' in attrs:
             attrs['placeholder'] = form._cw._(attrs['placeholder'])
@@ -387,7 +385,7 @@
     string.
     """
     type = 'hidden'
-    setdomid = False # by default, don't set id attribute on hidden input
+    setdomid = False  # by default, don't set id attribute on hidden input
     settabindex = False
 
 
@@ -474,7 +472,7 @@
                 options.append(tags.option(label, value=value, **oattrs))
         if optgroup_opened:
             options.append(u'</optgroup>')
-        if not 'size' in attrs:
+        if 'size' not in attrs:
             if self._multiple:
                 size = text_type(min(self.default_size, len(vocab) or 1))
             else:
@@ -532,12 +530,11 @@
                     options.append(tags.option(label, value=value))
         if 'size' not in attrs:
             attrs['size'] = self.default_size
-        if 'id' in attrs :
+        if 'id' in attrs:
             attrs.pop('id')
         return tags.select(name=name, multiple=self._multiple, id=name,
                            options=options, **attrs) + '\n'.join(inputs)
 
-
     def _render(self, form, field, renderer):
         domid = field.dom_id(form)
         jsnodes = {'widgetid': domid,
@@ -549,10 +546,10 @@
         return (self.template %
                 {'widgetid': jsnodes['widgetid'],
                  # helpinfo select tag
-                 'inoutinput' : self.render_select(form, field, jsnodes['from']),
+                 'inoutinput': self.render_select(form, field, jsnodes['from']),
                  # select tag with resultats
-                 'resinput' : self.render_select(form, field, jsnodes['to'], selected=True),
-                 'addinput' : self.add_button % jsnodes,
+                 'resinput': self.render_select(form, field, jsnodes['to'], selected=True),
+                 'addinput': self.add_button % jsnodes,
                  'removeinput': self.remove_button % jsnodes
                  })
 
@@ -673,21 +670,43 @@
 <img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""
                 % (helperid, inputid, year, month,
                    form._cw.uiprops['CALENDAR_ICON'],
-                   form._cw._('calendar'), helperid) )
+                   form._cw._('calendar'), helperid))
 
 
 class JQueryDatePicker(FieldWidget):
     """Use jquery.ui.datepicker to define a date picker. Will return the date as
     a unicode string.
+
+    You can couple DatePickers by using the min_of and/or max_of parameters.
+    The DatePicker identified by the value of min_of(/max_of) will force the user to
+    choose a date anterior(/posterior) to this DatePicker.
+
+    example:
+    start and end are two JQueryDatePicker and start must always be before end
+        affk.set_field_kwargs(etype, 'start_date', widget=JQueryDatePicker(min_of='end_date'))
+        affk.set_field_kwargs(etype, 'end_date', widget=JQueryDatePicker(max_of='start_date'))
+    That way, on change of end(/start) value a new max(/min) will be set for start(/end)
+    The invalid dates will be gray colored in the datepicker
     """
     needs_js = ('jquery.ui.js', )
     needs_css = ('jquery.ui.css',)
     default_size = 10
 
-    def __init__(self, datestr=None, **kwargs):
+    def __init__(self, datestr=None, min_of=None, max_of=None, **kwargs):
         super(JQueryDatePicker, self).__init__(**kwargs)
+        self.min_of = min_of
+        self.max_of = max_of
         self.value = datestr
 
+    def attributes(self, form, field):
+        form._cw.add_js('cubicweb.widgets.js')
+        attrs = super(JQueryDatePicker, self).attributes(form, field)
+        if self.max_of:
+            attrs['data-max-of'] = '%s-subject:%s' % (self.max_of, form.edited_entity.eid)
+        if self.min_of:
+            attrs['data-min-of'] = '%s-subject:%s' % (self.min_of, form.edited_entity.eid)
+        return attrs
+
     def _render(self, form, field, renderer):
         req = form._cw
         if req.lang != 'en':
@@ -695,11 +714,19 @@
         domid = field.dom_id(form, self.suffix)
         # XXX find a way to understand every format
         fmt = req.property_value('ui.date-format')
-        fmt = fmt.replace('%Y', 'yy').replace('%m', 'mm').replace('%d', 'dd')
-        req.add_onload(u'cw.jqNode("%s").datepicker('
-                       '{buttonImage: "%s", dateFormat: "%s", firstDay: 1,'
-                       ' showOn: "button", buttonImageOnly: true})' % (
-                           domid, req.uiprops['CALENDAR_ICON'], fmt))
+        picker_fmt = fmt.replace('%Y', 'yy').replace('%m', 'mm').replace('%d', 'dd')
+        max_date = min_date = None
+        if self.min_of:
+            current = getattr(form.edited_entity, self.min_of)
+            if current is not None:
+                max_date = current.strftime(fmt)
+        if self.max_of:
+            current = getattr(form.edited_entity, self.max_of)
+            if current is not None:
+                min_date = current.strftime(fmt)
+        req.add_onload(u'renderJQueryDatePicker("%s", "%s", "%s", %s, %s);'
+                       % (domid, req.uiprops['CALENDAR_ICON'], picker_fmt, json_dumps(min_date),
+                          json_dumps(max_date)))
         return self._render_input(form, field)
 
     def _render_input(self, form, field):
@@ -729,7 +756,7 @@
     def _render(self, form, field, renderer):
         domid = field.dom_id(form, self.suffix)
         form._cw.add_onload(u'cw.jqNode("%s").timePicker({step: %s, separator: "%s"})' % (
-                domid, self.timesteps, self.separator))
+            domid, self.timesteps, self.separator))
         return self._render_input(form, field)
 
 
@@ -769,8 +796,8 @@
         timepicker = JQueryTimePicker(timestr=timestr, timesteps=self.timesteps,
                                       suffix='time')
         return u'<div id="%s">%s%s</div>' % (field.dom_id(form),
-                                            datepicker.render(form, field, renderer),
-                                            timepicker.render(form, field, renderer))
+                                             datepicker.render(form, field, renderer),
+                                             timepicker.render(form, field, renderer))
 
     def process_field_data(self, form, field):
         req = form._cw
@@ -857,7 +884,6 @@
                                     pageid=entity._cw.pageid)
 
 
-
 class StaticFileAutoCompletionWidget(AutoCompletionWidget):
     """XXX describe me"""
     wdgtype = 'StaticFileSuggestField'
@@ -876,10 +902,11 @@
 
     def values_and_attributes(self, form, field):
         """override values_and_attributes to handle initial displayed values"""
-        values, attrs = super(LazyRestrictedAutoCompletionWidget, self).values_and_attributes(form, field)
+        values, attrs = super(LazyRestrictedAutoCompletionWidget, self).values_and_attributes(
+            form, field)
         assert len(values) == 1, "multiple selection is not supported yet by LazyWidget"
         if not values[0]:
-            values = form.cw_extra_kwargs.get(field.name,'')
+            values = form.cw_extra_kwargs.get(field.name, '')
             if not isinstance(values, (tuple, list)):
                 values = (values,)
         try:
@@ -919,7 +946,7 @@
             actual_fields[0].render(form, renderer),
             form._cw._('to_interval_end'),
             actual_fields[1].render(form, renderer),
-            )
+        )
 
 
 class HorizontalLayoutWidget(FieldWidget):
@@ -930,7 +957,7 @@
         if self.attrs.get('display_label', True):
             subst = self.attrs.get('label_input_substitution', '%(label)s %(input)s')
             fields = [subst % {'label': renderer.render_label(form, f),
-                              'input': f.render(form, renderer)}
+                               'input': f.render(form, renderer)}
                       for f in field.subfields(form)]
         else:
             fields = [f.render(form, renderer) for f in field.subfields(form)]
@@ -948,7 +975,7 @@
         assert self.suffix is None, 'not supported'
         req = form._cw
         pathqname = field.input_name(form, 'path')
-        fqsqname = field.input_name(form, 'fqs') # formatted query string
+        fqsqname = field.input_name(form, 'fqs')  # formatted query string
         if pathqname in form.form_previous_values:
             path = form.form_previous_values[pathqname]
             fqs = form.form_previous_values[fqsqname]
@@ -969,7 +996,7 @@
         attrs = dict(self.attrs)
         if self.setdomid:
             attrs['id'] = field.dom_id(form)
-        if self.settabindex and not 'tabindex' in attrs:
+        if self.settabindex and 'tabindex' not in attrs:
             attrs['tabindex'] = req.next_tabindex()
         # ensure something is rendered
         inputs = [u'<table><tr><th>',
@@ -1009,7 +1036,8 @@
                         try:
                             key, val = line.split('=', 1)
                         except ValueError:
-                            raise ProcessFormError(req._("wrong query parameter line %s") % (i+1))
+                            msg = req._("wrong query parameter line %s") % (i + 1)
+                            raise ProcessFormError(msg)
                         # value will be url quoted by build_url_params
                         values.setdefault(key, []).append(val)
         if not values:
@@ -1057,7 +1085,7 @@
             attrs['name'] = self.name
             if self.setdomid:
                 attrs['id'] = self.name
-        if self.settabindex and not 'tabindex' in attrs:
+        if self.settabindex and 'tabindex' not in attrs:
             attrs['tabindex'] = form._cw.next_tabindex()
         if self.icon:
             img = tags.img(src=form._cw.uiprops[self.icon], alt=self.icon)
@@ -1094,5 +1122,5 @@
         imgsrc = form._cw.uiprops[self.imgressource]
         return '<a id="%(domid)s" href="%(href)s">'\
                '<img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % {
-            'label': label, 'imgsrc': imgsrc,
-            'domid': self.domid, 'href': self.href}
+                   'label': label, 'imgsrc': imgsrc,
+                   'domid': self.domid, 'href': self.href}