# HG changeset patch # User Sylvain Thénault # Date 1250149761 -7200 # Node ID de0fcdb65e30ebfb4b22b95b436ff12cef25b468 # Parent 14d2c69e12c42fafe88394912d440f07f80a225b remove code deprecated in 3.2 diff -r 14d2c69e12c4 -r de0fcdb65e30 selectors.py --- a/selectors.py Thu Aug 13 09:41:24 2009 +0200 +++ b/selectors.py Thu Aug 13 09:49:21 2009 +0200 @@ -46,7 +46,6 @@ from warnings import warn from logilab.common.compat import all -from logilab.common.deprecation import deprecated from logilab.common.interface import implements as implements_iface from yams import BASE_TYPES @@ -963,123 +962,6 @@ self.score_entity = scorefunc -# XXX DEPRECATED in 3.4 ######################################################## -from cubicweb.vregistry import chainall - -yes_selector = deprecated()(yes) -norset_selector = deprecated()(none_rset) -rset_selector = deprecated()(any_rset) -anyrset_selector = deprecated()(nonempty_rset) -emptyrset_selector = deprecated()(empty_rset) -onelinerset_selector = deprecated()(one_line_rset) -twolinerset_selector = deprecated()(two_lines_rset) -twocolrset_selector = deprecated()(two_cols_rset) -largerset_selector = deprecated()(paginated_rset) -sortedrset_selector = deprecated()(sorted_rset) -oneetyperset_selector = deprecated()(one_etype_rset) -multitype_selector = deprecated()(two_etypes_rset) -anonymous_selector = deprecated()(anonymous_user) -not_anonymous_selector = deprecated()(authenticated_user) -primaryview_selector = deprecated()(primary_view) -contextprop_selector = deprecated()(match_context_prop) - -@deprecated('[3.4] use non_final_entity instead of %s') -def nfentity_selector(cls, req, rset=None, row=None, col=0, **kwargs): - return non_final_entity()(cls, req, rset, row, col) - -@deprecated('[3.4] use implements instead of %s') -def implement_interface(cls, req, rset=None, row=None, col=0, **kwargs): - return implements(*cls.accepts_interfaces)(cls, req, rset, row, col) -_interface_selector = deprecated()(implement_interface) -interface_selector = deprecated()(implement_interface) - -@deprecated('[3.4] use specified_etype_implements instead of %s') -def accept_etype(cls, req, *args, **kwargs): - """check etype presence in request form *and* accepts conformance""" - return specified_etype_implements(*cls.accepts)(cls, req, *args) -etype_form_selector = accept_etype - -@deprecated('[3.4] use match_search_state instead of %s') -def searchstate_selector(cls, req, rset=None, row=None, col=0, **kwargs): - return match_search_state(cls.search_states)(cls, req, rset, row, col) - -@deprecated('[3.4] use match_user_groups instead of %s') -def match_user_group(cls, req, rset=None, row=None, col=0, **kwargs): - return match_user_groups(*cls.require_groups)(cls, req, rset, row, col, **kwargs) -in_group_selector = match_user_group - -@deprecated('[3.4] use relation_possible instead of %s') -def has_relation(cls, req, rset=None, row=None, col=0, **kwargs): - return relation_possible(cls.rtype, role(cls), cls.etype, - getattr(cls, 'require_permission', 'read'))(cls, req, rset, row, col, **kwargs) - -@deprecated('[3.4] use relation_possible instead of %s') -def one_has_relation(cls, req, rset=None, row=None, col=0, **kwargs): - return relation_possible(cls.rtype, role(cls), cls.etype, - getattr(cls, 'require_permission', 'read', - once_is_enough=True))(cls, req, rset, row, col, **kwargs) - -@deprecated('[3.4] use implements instead of %s') -def accept_rset(cls, req, rset=None, row=None, col=0, **kwargs): - """simply delegate to cls.accept_rset method""" - return implements(*cls.accepts)(cls, req, rset, row=row, col=col) -accept_rset_selector = accept_rset - -accept = chainall(non_final_entity(), accept_rset, name='accept') -accept = deprecated('[3.4] use implements selector')(accept) -accept_selector = deprecated()(accept) - -accept_one = deprecated()(chainall(one_line_rset, accept, - name='accept_one')) -accept_one_selector = deprecated()(accept_one) - - -def _rql_condition(cls, req, rset=None, row=None, col=0, **kwargs): - if cls.condition: - return rql_condition(cls.condition)(cls, req, rset, row, col) - return 1 -_rqlcondition_selector = deprecated()(_rql_condition) - -rqlcondition_selector = deprecated()(chainall(non_final_entity(), one_line_rset, _rql_condition, - name='rql_condition')) - -@deprecated('[3.4] use but_etype instead of %s') -def but_etype_selector(cls, req, rset=None, row=None, col=0, **kwargs): - return but_etype(cls.etype)(cls, req, rset, row, col) - -@lltrace -def etype_rtype_selector(cls, req, rset=None, row=None, col=0, **kwargs): - schema = cls.schema - perm = getattr(cls, 'require_permission', 'read') - if hasattr(cls, 'etype'): - eschema = schema.eschema(cls.etype) - if not (eschema.has_perm(req, perm) or eschema.has_local_role(perm)): - return 0 - if hasattr(cls, 'rtype'): - rschema = schema.rschema(cls.rtype) - if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)): - return 0 - return 1 -etype_rtype_selector = deprecated()(etype_rtype_selector) - -#req_form_params_selector = deprecated()(match_form_params) # form_params -#kwargs_selector = deprecated()(match_kwargs) # expected_kwargs - -# compound selectors ########################################################## - -searchstate_accept = chainall(nonempty_rset(), accept, - name='searchstate_accept') -searchstate_accept_selector = deprecated()(searchstate_accept) - -searchstate_accept_one = chainall(one_line_rset, accept, _rql_condition, - name='searchstate_accept_one') -searchstate_accept_one_selector = deprecated()(searchstate_accept_one) - -searchstate_accept = deprecated()(searchstate_accept) -searchstate_accept_one = deprecated()(searchstate_accept_one) - -# end of deprecation section ################################################## - def unbind_method(selector): def new_selector(registered): # get the unbound method diff -r 14d2c69e12c4 -r de0fcdb65e30 web/views/baseforms.py --- a/web/views/baseforms.py Thu Aug 13 09:41:24 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,607 +0,0 @@ -"""Set of HTML automatic forms to create, delete, copy or edit a single entity -or a list of entities of the same type - -: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 copy import copy - -from simplejson import dumps - -from logilab.mtconverter import xml_escape -from logilab.common.decorators import cached - -from cubicweb.selectors import (specified_etype_implements, accepts_etype_compat, - non_final_entity, match_kwargs, one_line_rset) -from cubicweb.view import View, EntityView -from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param -from cubicweb.web.controller import NAV_FORM_PARAMETERS -from cubicweb.web.widgets import checkbox, InputWidget, ComboBoxWidget -from cubicweb.web.form import FormMixIn -from cubicweb.web.views.autoform import AutomaticEntityForm - -_ = unicode - - -class EditionForm(FormMixIn, EntityView): - """primary entity edition form - - When generating a new attribute_input, the editor will look for a method - named 'default_ATTRNAME' on the entity instance, where ATTRNAME is the - name of the attribute being edited. You may use this feature to compute - dynamic default values such as the 'tomorrow' date or the user's login - being connected - """ - id = 'edition' - __select__ = one_line_rset() & non_final_entity() - - title = _('edition') - controller = 'edit' - skip_relations = set() - - EDITION_BODY = u'''\ - %(errormsg)s -
- %(title)s -
%(inprogress)s
-
%(mainattrs_label)s
-
- %(base)s - %(attrform)s - %(relattrform)s -
- %(relform)s -
- - - - -
- %(validate)s - - %(apply)s - %(cancel)s -
-
-''' - - def cell_call(self, row, col, **kwargs): - self.req.add_js( ('cubicweb.ajax.js', ) ) - entity = self.complete_entity(row, col) - self.edit_form(entity, kwargs) - - def edit_form(self, entity, kwargs): - varmaker = self.req.get_page_data('rql_varmaker') - if varmaker is None: - varmaker = self.req.varmaker - self.req.set_page_data('rql_varmaker', varmaker) - self.varmaker = varmaker - self.w(self.EDITION_BODY % self.form_context(entity, kwargs)) - - def form_context(self, entity, kwargs): - """returns the dictionnary used to fill the EDITION_BODY template - - If you create your own edition form, you can probably just override - `EDITION_BODY` and `form_context` - """ - if self.need_multipart(entity): - enctype = 'multipart/form-data' - else: - enctype = 'application/x-www-form-urlencoded' - self._hiddens = [] - if entity.eid is None: - entity.eid = self.varmaker.next() - # XXX (hack) action_title might need __linkto req's original value - # and widgets such as DynamicComboWidget might change it - # so we need to compute title before calling atttributes_form - formtitle = self.action_title(entity) - # be sure to call .*_form first so tabindexes are correct and inlined - # fields errors are consumed - if not entity.has_eid() or entity.has_perm('update'): - attrform = self.attributes_form(entity, kwargs) - else: - attrform = '' - inlineform = self.inline_entities_form(entity, kwargs) - relform = self.relations_form(entity, kwargs) - vindex = self.req.next_tabindex() - aindex = self.req.next_tabindex() - cindex = self.req.next_tabindex() - self.add_hidden_web_behaviour_params(entity) - _ = self.req._ - return { - 'formid' : self.domid, - 'onsubmit' : self.on_submit(entity), - 'enctype' : enctype, - 'errormsg' : self.error_message(), - 'action' : self.build_url('validateform'), - 'eids' : entity.has_eid() and [entity.eid] or [], - 'inprogress': _('validating...'), - 'title' : formtitle, - 'mainattrs_label' : _('main informations'), - 'reseturl' : self.redirect_url(entity), - 'attrform' : attrform, - 'relform' : relform, - 'relattrform': inlineform, - 'base' : self.base_form(entity, kwargs), - 'validate' : self.button_ok(tabindex=vindex), - 'apply' : self.button_apply(tabindex=aindex), - 'cancel' : self.button_cancel(tabindex=cindex), - } - - @property - def formid(self): - return self.id - - def action_title(self, entity): - """form's title""" - ptitle = self.req._(self.title) - return u'
%s %s
' % ( - entity.dc_type(), ptitle and '(%s)' % ptitle) - - - def base_form(self, entity, kwargs): - output = [] - for name, value, iid in self._hiddens: - if isinstance(value, basestring): - value = xml_escape(value) - if iid: - output.append(u'' - % (iid, name, value)) - else: - output.append(u'' - % (name, value)) - return u'\n'.join(output) - - def add_hidden_web_behaviour_params(self, entity): - """inserts hidden params controlling how errors and redirection - should be handled - """ - req = self.req - self._hiddens.append( (u'__maineid', entity.eid, u'') ) - self._hiddens.append( (u'__errorurl', req.url(), u'errorurl') ) - self._hiddens.append( (u'__form_id', self.formid, u'') ) - for param in NAV_FORM_PARAMETERS: - value = req.form.get(param) - if value: - self._hiddens.append( (param, value, u'') ) - msg = self.submited_message() - # If we need to directly attach the new object to another one - for linkto in req.list_form_param('__linkto'): - self._hiddens.append( ('__linkto', linkto, '') ) - msg = '%s %s' % (msg, self.req._('and linked')) - self._hiddens.append( ('__message', msg, '') ) - - - def attributes_form(self, entity, kwargs, include_eid=True): - """create a form to edit entity's attributes""" - html = [] - w = html.append - eid = entity.eid - wdg = entity.get_widget - lines = (wdg(rschema, x) for rschema, x in self.editable_attributes(entity)) - if include_eid: - self._hiddens.append( ('eid', entity.eid, '') ) - self._hiddens.append( (eid_param('__type', eid), entity.e_schema, '') ) - w(u'' % - (kwargs.get('tab_id', 'entityForm%s' % eid), - kwargs.get('tab_class', 'attributeForm'))) - for widget in lines: - w(u'\n' % widget.render_label(entity)) - error = widget.render_error(entity) - if error: - w(u'\n') - w(u'
%s') - else: - w(u'') - if error: - w(error) - w(widget.edit_render(entity)) - w(widget.render_help(entity)) - w(u'
') - return u'\n'.join(html) - - def editable_attributes(self, entity): - # XXX both (add, delete) - return [(rschema, x) for rschema, _, x in entity.relations_by_category(('primary', 'secondary'), 'add') - if rschema != 'eid'] - - def relations_form(self, entity, kwargs): - srels_by_cat = entity.srelations_by_category(('generic', 'metadata'), 'add') - if not srels_by_cat: - return u'' - req = self.req - _ = self.req._ - label = u'%s :' % _('This %s' % entity.e_schema).capitalize() - eid = entity.eid - html = [] - w = html.append - w(u'
') - w(u'%s' % label) - w(u'') - for row in self.relations_table(entity): - # already linked entities - if row[2]: - w(u'' % row[0].display_name(req, row[1])) - w(u'') - w(u'') - pendings = list(self.restore_pending_inserts(entity)) - if not pendings: - w(u'') - else: - for row in pendings: - # soon to be linked to entities - w(u'' % row[1]) - w(u'' % row[3]) - w(u'') - w(u'') - w(u'' % eid) - w(u'') - w(u'' % eid) - w(u'') - w(u'
%s') - w(u'
    ') - for viewparams in row[2]: - w(u'' - % (viewparams[1], viewparams[0], viewparams[2], viewparams[3])) - if not self.force_display and self.maxrelitems < len(row[2]): - w(u'' % self.force_display_link()) - w(u'
') - w(u'
  
%s') - w(u'[x]' % - (_('cancel this insert'), row[2])) - w(u'%s' - % (row[1], row[4], xml_escape(row[5]))) - w(u'
') - w(u'%s' % _('add relation')) - w(u'') - w(u'
') - w(u'
') - return '\n'.join(html) - - def inline_entities_form(self, entity, kwargs): - """create a form to edit entity's inlined relations""" - result = [] - _ = self.req._ - for rschema, targettypes, x in entity.relations_by_category('inlineview', 'add'): - # show inline forms only if there's one possible target type - # for rschema - if len(targettypes) != 1: - self.warning('entity related by the %s relation should have ' - 'inlined form but there is multiple target types, ' - 'dunno what to do', rschema) - continue - targettype = targettypes[0].type - if self.should_inline_relation_form(entity, rschema, targettype, x): - result.append(u'
' % rschema) - existant = entity.has_eid() and entity.related(rschema) - if existant: - # display inline-edition view for all existing related entities - result.append(self.view('inline-edition', existant, - ptype=entity.e_schema, peid=entity.eid, - rtype=rschema, role=x, **kwargs)) - if x == 'subject': - card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0] - else: - card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1] - # there is no related entity and we need at least one : we need to - # display one explicit inline-creation view - if self.should_display_inline_relation_form(rschema, existant, card): - result.append(self.view('inline-creation', None, etype=targettype, - peid=entity.eid, ptype=entity.e_schema, - rtype=rschema, role=x, **kwargs)) - # we can create more than one related entity, we thus display a link - # to add new related entities - if self.should_display_add_inline_relation_link(rschema, existant, card): - divid = "addNew%s%s%s:%s" % (targettype, rschema, x, entity.eid) - result.append(u'
' - % divid) - js = "addInlineCreationForm('%s', '%s', '%s', '%s', '%s')" % ( - entity.eid, entity.e_schema, targettype, rschema, x) - if card in '1?': - js = "toggleVisibility('%s'); %s" % (divid, js) - result.append(u'+ %s.' - % (rschema, entity.eid, js, - self.req.__('add a %s' % targettype))) - result.append(u'
') - result.append(u'
 
') - result.append(u'
') - return '\n'.join(result) - - # should_* method extracted to allow overriding - - def should_inline_relation_form(self, entity, rschema, targettype, role): - return AutomaticEntityForm.rinlined.etype_get(entity.id, rschema, role, - targettype) - - def should_display_inline_relation_form(self, rschema, existant, card): - return not existant and card in '1+' - - def should_display_add_inline_relation_link(self, rschema, existant, card): - return not existant or card in '+*' - - def reset_url(self, entity): - return entity.absolute_url() - - def on_submit(self, entity): - return u'return freezeFormButtons(\'%s\')' % (self.domid) - - def submited_message(self): - return self.req._('element edited') - - - -class CreationForm(EditionForm): - __select__ = specified_etype_implements('Any') - # XXX bw compat, use View.registered since we don't want accept_compat - # wrapper set in EntityView - registered = accepts_etype_compat(View.registered) - id = 'creation' - title = _('creation') - - def call(self, **kwargs): - """creation view for an entity""" - self.req.add_js( ('cubicweb.ajax.js',) ) - self.initialize_varmaker() - etype = kwargs.pop('etype', self.req.form.get('etype')) - try: - entity = self.vreg.etype_class(etype)(self.req, None, None) - except: - self.w(self.req._('no such entity type %s') % etype) - else: - entity.eid = self.varmaker.next() - self.edit_form(entity, kwargs) - - def action_title(self, entity): - """custom form title if creating a entity with __linkto""" - if '__linkto' in self.req.form: - if isinstance(self.req.form['__linkto'], list): - # XXX which one should be considered (case: add a ticket to a version in jpl) - rtype, linkto_eid, role = self.req.form['__linkto'][0].split(':') - else: - rtype, linkto_eid, role = self.req.form['__linkto'].split(':') - linkto_rset = self.req.eid_rset(linkto_eid) - linkto_type = linkto_rset.description[0][0] - if role == 'subject': - title = self.req.__('creating %s (%s %s %s %%(linkto)s)' % ( - entity.e_schema, entity.e_schema, rtype, linkto_type)) - else: - title = self.req.__('creating %s (%s %%(linkto)s %s %s)' % ( - entity.e_schema, linkto_type, rtype, entity.e_schema)) - msg = title % {'linkto' : self.view('incontext', linkto_rset)} - return u'
%s
' % msg - else: - return super(CreationForm, self).action_title(entity) - - @property - def formid(self): - return 'edition' - - def relations_form(self, entity, kwargs): - return u'' - - def reset_url(self, entity=None): - return self.build_url(self.req.form.get('etype', '').lower()) - - def submited_message(self): - return self.req._('element created') - - def url(self): - """return the url associated with this view""" - return self.create_url(self.req.form.get('etype')) - - -class InlineFormMixIn(object): - - @cached - def card(self, etype): - return self.rschema.rproperty(self.parent_schema, etype, 'cardinality')[0] - - def action_title(self, entity): - return self.rschema.display_name(self.req, self.role) - - def add_hidden_web_behaviour_params(self, entity): - pass - - def edit_form(self, entity, ptype, peid, rtype, - role='subject', **kwargs): - self.rschema = self.schema.rschema(rtype) - self.role = role - self.parent_schema = self.schema.eschema(ptype) - self.parent_eid = peid - super(InlineFormMixIn, self).edit_form(entity, kwargs) - - def should_inline_relation_form(self, entity, rschema, targettype, role): - if rschema == self.rschema: - return False - return AutomaticEntityForm.rinlined.etype_get(entity.id, rschema, role, - targettype) - - @cached - def keep_entity(self, entity): - req = self.req - # are we regenerating form because of a validation error ? - erroneous_post = req.data.get('formvalues') - if erroneous_post: - cdvalues = req.list_form_param('%s:%s' % (self.rschema, - self.parent_eid), - erroneous_post) - if unicode(entity.eid) not in cdvalues: - return False - return True - - def form_context(self, entity, kwargs): - ctx = super(InlineFormMixIn, self).form_context(entity, kwargs) - _ = self.req._ - local_ctx = {'createmsg' : self.req.__('add a %s' % entity.e_schema), - 'so': self.role[0], # 's' for subject, 'o' for object - 'eid' : entity.eid, - 'rtype' : self.rschema, - 'parenteid' : self.parent_eid, - 'parenttype' : self.parent_schema, - 'etype' : entity.e_schema, - 'novalue' : INTERNAL_FIELD_VALUE, - 'removemsg' : self.req.__('remove this %s' % entity.e_schema), - 'notice' : self.req._('click on the box to cancel the deletion'), - } - ctx.update(local_ctx) - return ctx - - -class CopyEditionForm(EditionForm): - id = 'copy' - title = _('copy edition') - - def cell_call(self, row, col, **kwargs): - self.req.add_js(('cubicweb.ajax.js',)) - entity = self.complete_entity(row, col, skip_bytes=True) - # make a copy of entity to avoid altering the entity in the - # request's cache. - self.newentity = copy(entity) - self.copying = self.newentity.eid - self.newentity.eid = None - self.edit_form(self.newentity, kwargs) - del self.newentity - - def action_title(self, entity): - """form's title""" - msg = super(CopyEditionForm, self).action_title(entity) - return msg + (u'\n' - % self.req._('Please note that this is only a shallow copy')) - # XXX above message should have style of a warning - - @property - def formid(self): - return 'edition' - - def relations_form(self, entity, kwargs): - return u'' - - def reset_url(self, entity): - return self.build_url('view', rql='Any X WHERE X eid %s' % self.copying) - - def attributes_form(self, entity, kwargs, include_eid=True): - # we don't want __clone_eid on inlined edited entities - if entity.eid == self.newentity.eid: - self._hiddens.append((eid_param('__cloned_eid', entity.eid), self.copying, '')) - return EditionForm.attributes_form(self, entity, kwargs, include_eid) - - def submited_message(self): - return self.req._('element copied') - - -class TableEditForm(FormMixIn, EntityView): - id = 'muledit' - title = _('multiple edit') - - EDITION_BODY = u'''
- %(error)s -
%(progress)s
-
- - - - - - - - %(attrheaders)s - - %(lines)s -
- - - - -
- - -
-
-
-''' - - WIDGET_CELL = u'''\ - - %(error)s -
%(widget)s
-''' - - def call(self, **kwargs): - """a view to edit multiple entities of the same type - the first column should be the eid - """ - req = self.req - form = req.form - _ = req._ - sampleentity = self.complete_entity(0) - attrheaders = [u'%s' % rdef[0].display_name(req, rdef[-1]) - for rdef in sampleentity.relations_by_category('primary', 'add') - if rdef[0].type != 'eid'] - ctx = {'action' : self.build_url('edit'), - 'error': self.error_message(), - 'progress': _('validating...'), - 'url': xml_escape(req.url()), - 'formid': self.id, - 'redirectvid': xml_escape(form.get('__redirectvid', 'list')), - 'redirectrql': xml_escape(form.get('__redirectrql', self.rset.printable_rql())), - 'attrheaders': u'\n'.join(attrheaders), - 'lines': u'\n'.join(self.edit_form(ent) for ent in self.rset.entities()), - 'okvalue': _('button_ok').capitalize(), - 'oktitle': _('validate modifications on selected items').capitalize(), - 'cancelvalue': _('button_reset').capitalize(), - 'canceltitle': _('revert changes').capitalize(), - } - self.w(self.EDITION_BODY % ctx) - - - def reset_url(self, entity=None): - self.build_url('view', rql=self.rset.printable_rql()) - - def edit_form(self, entity): - html = [] - w = html.append - entity.complete() - eid = entity.eid - values = self.req.data.get('formvalues', ()) - qeid = eid_param('eid', eid) - checked = qeid in values - w(u'' % (entity.row % 2 and u'even' or u'odd')) - w(u'%s' - % (checkbox('eid', eid, checked=checked), eid, entity.e_schema)) - # attribute relations (skip eid which is handled by the checkbox - wdg = entity.get_widget - wdgfactories = [wdg(rschema, x) for rschema, _, x in entity.relations_by_category('primary', 'add') - if rschema.type != 'eid'] # XXX both (add, delete) - seid = xml_escape(dumps(eid)) - for wobj in wdgfactories: - if isinstance(wobj, ComboBoxWidget): - wobj.attrs['onchange'] = "setCheckboxesState2('eid', %s, 'checked')" % seid - elif isinstance(wobj, InputWidget): - wobj.attrs['onkeypress'] = "setCheckboxesState2('eid', %s, 'checked')" % seid - error = wobj.render_error(entity) - if error: - csscls = u' class="error"' - else: - csscls = u'' - w(self.WIDGET_CELL % {'csscls': csscls, 'error': error, - 'widget': wobj.edit_render(entity)}) - w(u'') - return '\n'.join(html) - - -# XXX bw compat - -from logilab.common.deprecation import class_moved -from cubicweb.web.views import editviews -ComboboxView = class_moved(editviews.ComboboxView) 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())