diff -r 14d2c69e12c4 -r de0fcdb65e30 web/widgets.py --- a/web/widgets.py Thu Aug 13 09:41:24 2009 +0200 +++ /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-2009 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'' % ( - 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'' % (forattr, label) - else: - label = u'%s' % (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'%s' % errex.errors[self.name] - return u'' - - def render_help(self, entity): - """render a help message about the (edited) field""" - req = entity.req - help = [u'
'] - descr = self.description or self.rschema.rproperty(self.subjtype, self.objtype, 'description') - if descr: - help.append(u'%s' % req._(descr)) - example = self.render_example(req) - if example: - help.append(u'(%s: %s)' - % (req._('sample format'), example)) - help.append(u'
') - return u' '.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.is_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'\n' % ( - editmark, self.rname, qvalue) - -class InputWidget(Widget): - """abstract class for input generating a 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' % ( - 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'' % 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' % { - '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
\n (%s)' % ( - 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'\n'\ - '\n' % ( - frname, format, frname) - return u'%s' % ( - 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
' % ( - 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'%s
' % (self.rname, attrs1, entity.req._('yes')), - u'%s
' % (self.rname, attrs2, entity.req._('no'))] - return '\n'.join(wdgs) - - -class FileWidget(Widget): - need_multipart = True - def _file_wdg(self, entity): - wdgs = [u'' % (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'%s' % - (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'') - if entity.has_eid(): - if not self.required(entity): - # trick to be able to delete an uploaded file - wdgs.append(u'
') - 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'
') - 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'

%s

' % 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'
') - 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'') - 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'') - res.append(u'
') - res.append(u'') - res.append(u' 
') - 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'
' ] - descr = self.rschema.rproperty(self.subjtype, self.objtype, 'description') - if descr: - help.append('%s' % req._(descr)) - example = self.render_example(req) - if example: - help.append('(%s: %s)' - % (req._('sample format'), example)) - help.append(u'
') - return u' '.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""" -""" - % (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'%s' % (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'%s' % (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'%s' % (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.is_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())