--- 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
--- 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
-<form id="%(formid)s" class="entityForm" cubicweb:target="eformframe"
- method="post" onsubmit="%(onsubmit)s" enctype="%(enctype)s" action="%(action)s">
- %(title)s
- <div id="progress">%(inprogress)s</div>
- <div class="iformTitle"><span>%(mainattrs_label)s</span></div>
- <div class="formBody"><fieldset>
- %(base)s
- %(attrform)s
- %(relattrform)s
-</fieldset>
- %(relform)s
- </div>
- <table width="100%%">
- <tbody>
- <tr><td align="center">
- %(validate)s
- </td><td style="align: right; width: 50%%;">
- %(apply)s
- %(cancel)s
- </td></tr>
- </tbody>
- </table>
-</form>
-'''
-
- 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'<div class="formTitle"><span>%s %s</span></div>' % (
- 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'<input id="%s" type="hidden" name="%s" value="%s" />'
- % (iid, name, value))
- else:
- output.append(u'<input type="hidden" name="%s" value="%s" />'
- % (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'<table id="%s" class="%s" style="width:100%%;">' %
- (kwargs.get('tab_id', 'entityForm%s' % eid),
- kwargs.get('tab_class', 'attributeForm')))
- for widget in lines:
- w(u'<tr>\n<th class="labelCol">%s</th>' % widget.render_label(entity))
- error = widget.render_error(entity)
- if error:
- w(u'<td class="error" style="width:100%;">')
- else:
- w(u'<td style="width:100%;">')
- if error:
- w(error)
- w(widget.edit_render(entity))
- w(widget.render_help(entity))
- w(u'</td>\n</tr>')
- w(u'</table>')
- 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'<fieldset class="subentity">')
- w(u'<legend class="iformTitle">%s</legend>' % label)
- w(u'<table id="relatedEntities">')
- for row in self.relations_table(entity):
- # already linked entities
- if row[2]:
- w(u'<tr><th class="labelCol">%s</th>' % row[0].display_name(req, row[1]))
- w(u'<td>')
- w(u'<ul>')
- for viewparams in row[2]:
- w(u'<li class="invisible">%s<div id="span%s" class="%s">%s</div></li>'
- % (viewparams[1], viewparams[0], viewparams[2], viewparams[3]))
- if not self.force_display and self.maxrelitems < len(row[2]):
- w(u'<li class="invisible">%s</li>' % self.force_display_link())
- w(u'</ul>')
- w(u'</td>')
- w(u'</tr>')
- pendings = list(self.restore_pending_inserts(entity))
- if not pendings:
- w(u'<tr><th> </th><td> </td></tr>')
- else:
- for row in pendings:
- # soon to be linked to entities
- w(u'<tr id="tr%s">' % row[1])
- w(u'<th>%s</th>' % row[3])
- w(u'<td>')
- w(u'<a class="handle" title="%s" href="%s">[x]</a>' %
- (_('cancel this insert'), row[2]))
- w(u'<a id="a%s" class="editionPending" href="%s">%s</a>'
- % (row[1], row[4], xml_escape(row[5])))
- w(u'</td>')
- w(u'</tr>')
- w(u'<tr id="relationSelectorRow_%s" class="separator">' % eid)
- w(u'<th class="labelCol">')
- w(u'<span>%s</span>' % _('add relation'))
- w(u'<select id="relationSelector_%s" tabindex="%s" onchange="javascript:showMatchingSelect(this.options[this.selectedIndex].value,%s);">'
- % (eid, req.next_tabindex(), xml_escape(dumps(eid))))
- w(u'<option value="">%s</option>' % _('select a relation'))
- for i18nrtype, rschema, target in srels_by_cat:
- # more entities to link to
- w(u'<option value="%s_%s">%s</option>' % (rschema, target, i18nrtype))
- w(u'</select>')
- w(u'</th>')
- w(u'<td id="unrelatedDivs_%s"></td>' % eid)
- w(u'</tr>')
- w(u'</table>')
- w(u'</fieldset>')
- 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'<div id="inline%sslot">' % 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'<div class="inlinedform" id="%s" cubicweb:limit="true">'
- % 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'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
- % (rschema, entity.eid, js,
- self.req.__('add a %s' % targettype)))
- result.append(u'</div>')
- result.append(u'<div class="trame_grise"> </div>')
- result.append(u'</div>')
- 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'<div class="formTitle notransform"><span>%s</span></div>' % 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'<script type="text/javascript">updateMessage("%s");</script>\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'''<form method="post" id="entityForm" onsubmit="return validateForm('entityForm', null);" action="%(action)s">
- %(error)s
- <div id="progress">%(progress)s</div>
- <fieldset>
- <input type="hidden" name="__errorurl" value="%(url)s" />
- <input type="hidden" name="__form_id" value="%(formid)s" />
- <input type="hidden" name="__redirectvid" value="%(redirectvid)s" />
- <input type="hidden" name="__redirectrql" value="%(redirectrql)s" />
- <table class="listing">
- <tr class="header">
- <th align="left"><input type="checkbox" onclick="setCheckboxesState('eid', this.checked)" value="" title="toggle check boxes" /></th>
- %(attrheaders)s
- </tr>
- %(lines)s
- </table>
- <table width="100%%">
- <tr>
- <td align="left">
- <input class="validateButton" type="submit" value="%(okvalue)s" title="%(oktitle)s" />
- <input class="validateButton" type="reset" name="__action_cancel" value="%(cancelvalue)s" title="%(canceltitle)s" />
- </td>
- </tr>
- </table>
- </fieldset>
-</form>
-'''
-
- WIDGET_CELL = u'''\
-<td%(csscls)s>
- %(error)s
- <div>%(widget)s</div>
-</td>'''
-
- 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'<th>%s</th>' % 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'<tr class="%s">' % (entity.row % 2 and u'even' or u'odd'))
- w(u'<td>%s<input type="hidden" name="__type:%s" value="%s" /></td>'
- % (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'</tr>')
- 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)
--- 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'<input type="checkbox" name="%s" value="%s" %s %s />' % (
- 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'<label class="required"%s>%s</label>' % (forattr, label)
- else:
- label = u'<label%s>%s</label>' % (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'<span class="error">%s</span>' % errex.errors[self.name]
- return u''
-
- def render_help(self, entity):
- """render a help message about the (edited) field"""
- req = entity.req
- help = [u'<div class="helper">']
- descr = self.description or self.rschema.rproperty(self.subjtype, self.objtype, 'description')
- if descr:
- help.append(u'<span>%s</span>' % req._(descr))
- example = self.render_example(req)
- if example:
- help.append(u'<span>(%s: %s)</span>'
- % (req._('sample format'), example))
- help.append(u'</div>')
- 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'<input type="hidden" name="%s-%s" value="%s"/>\n' % (
- editmark, self.rname, qvalue)
-
-class InputWidget(Widget):
- """abstract class for input generating a <input> 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<input type="%s" name="%s" value="%s" %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'<input type="hidden" name="eid" value="%s" />' % 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<input type="text" name="%(iid)s" value="%(value)s" cubicweb:dataurl="%(url)s" class="widget%(required)s" id="%(iid)s" '
- u'tabindex="%(tabindex)s" cubicweb:loadtype="auto" cubicweb:wdgtype="%(wdgtype)s" %(attrs)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<br/>\n<input type="%s" name="%s" id="%s" tabindex="%s"/> <span class="emphasis">(%s)</span>' % (
- 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'<input type="hidden" name="edits-%s" value="%s"/>\n'\
- '<input type="hidden" name="%s" value="text/html"/>\n' % (
- frname, format, frname)
- return u'%s<textarea cubicweb:type="wysiwyg" onkeyup="autogrow(this)" name="%s" %s>%s</textarea>' % (
- 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<br/><textarea onkeyup="autogrow(this)" name="%s" %s>%s</textarea>' % (
- 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'<input type="radio" name="%s" value="1" %s/>%s<br/>' % (self.rname, attrs1, entity.req._('yes')),
- u'<input type="radio" name="%s" value="" %s/>%s<br/>' % (self.rname, attrs2, entity.req._('no'))]
- return '\n'.join(wdgs)
-
-
-class FileWidget(Widget):
- need_multipart = True
- def _file_wdg(self, entity):
- wdgs = [u'<input type="file" name="%s" %s/>' % (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'<a href="%s" title="%s"><img src="%s" alt="%s"/></a>' %
- (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'<div id="%s" class="hidden">' % divid)
- for extraattr in ('_format', '_encoding'):
- if entity.e_schema.has_subject_relation('%s%s' % (self.name, extraattr)):
- ewdg = entity.get_widget(self.name + extraattr)
- wdgs.append(ewdg.render_label(entity))
- wdgs.append(ewdg.edit_render(entity, includehelp=True))
- wdgs.append(u'<br/>')
- wdgs.append(u'</div>')
- if entity.has_eid():
- if not self.required(entity):
- # trick to be able to delete an uploaded file
- wdgs.append(u'<br/>')
- 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'<br/>')
- 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'<p><b>%s</b></p>' % 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'<br/>')
- 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'<select name="%s" %s>' % (self.rname, self.format_attrs()))
- for label, value in self.vocabulary(entity):
- if value is None:
- # handle separator
- res.append(u'<optgroup label="%s"/>' % (label or ''))
- else:
- value, flag = self.form_value(entity, value, dvalues)
- res.append(u'<option value="%s" %s>%s</option>' % (value, flag, xml_escape(label)))
- res.append(u'</select>')
- 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'<select class="widget" cubicweb:etype_to="%s" cubicweb:etype_from="%s" cubicweb:loadtype="auto" cubicweb:wdgtype="AddComboBox" name="%s" %s>'
- % (entity.e_schema, etype_from, self.rname, self.format_attrs()))
- for label, value in self.vocabulary(entity):
- if value is None:
- # handle separator
- res.append(u'<optgroup label="%s"/>' % (label or ''))
- else:
- value, flag = self.form_value(entity, value, dvalues)
- res.append(u'<option value="%s" %s>%s</option>' % (value, flag, xml_escape(label)))
- res.append(u'</select>')
- res.append(u'<div id="newvalue">')
- res.append(u'<input type="text" id="newopt" />')
- res.append(u'<a href="javascript:noop()" id="add_newopt"> </a></div>')
- 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'<div class="helper">' ]
- descr = self.rschema.rproperty(self.subjtype, self.objtype, 'description')
- if descr:
- help.append('<span>%s</span>' % req._(descr))
- example = self.render_example(req)
- if example:
- help.append('<span>(%s: %s)</span>'
- % (req._('sample format'), example))
- help.append(u'</div>')
- 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"""<a onclick="toggleCalendar('%s', '%s', %s, %s);" class="calhelper">
-<img src="%s" title="%s" alt="" /></a><div class="calpopup hidden" id="%s"></div>"""
- % (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'<a href="mailto:%s">%s</a>' % (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'<a href="%s">%s</a>' % (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'<a href="%s">%s</a>' % (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())