# HG changeset patch # User Sylvain Thénault # Date 1451924616 -3600 # Node ID b356acb36e152d3fd8d0561f93222aa158dd0ed0 # Parent bfd11ffa79f71e7b19b5adab682c4f6e846584bd# Parent 1f41697f2e26ce89fd8453c8451b8e975e480fea merge diff -r bfd11ffa79f7 -r b356acb36e15 web/data/cubicweb.widgets.js --- 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) * diff -r bfd11ffa79f7 -r b356acb36e15 web/formwidgets.py --- 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'') - 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 @@ """ % (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'
%s%s
' % (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'
', @@ -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 ''\ '%(label)s%(label)s' % { - 'label': label, 'imgsrc': imgsrc, - 'domid': self.domid, 'href': self.href} + 'label': label, 'imgsrc': imgsrc, + 'domid': self.domid, 'href': self.href}