--- a/web/views/reledit.py Wed Sep 15 11:13:17 2010 +0200
+++ b/web/views/reledit.py Wed Sep 15 15:00:04 2010 +0200
@@ -21,6 +21,7 @@
import copy
from logilab.mtconverter import xml_escape
+from logilab.common.deprecation import deprecated
from cubicweb import neg_role
from cubicweb.schema import display_name
@@ -39,8 +40,8 @@
return u''
def append_field(self, *args):
pass
- def field_by_name(self, rtype, role, eschema=None):
- return None
+
+rctrl = uicfg.reledit_ctrl
class ClickAndEditFormView(EntityView):
__regid__ = 'doreledit'
@@ -60,8 +61,11 @@
_editzonemsg = _('click to edit this field')
# default relation vids according to cardinality
+ # can be changed per rtype using reledit_ctrl rtag
_one_rvid = 'incontext'
_many_rvid = 'csv'
+ # renderer
+ _form_renderer_id = 'base'
def cell_call(self, row, col, rtype=None, role='subject',
reload=False, # controls reloading the whole page after change
@@ -69,89 +73,98 @@
# function taking the subject entity & returning a boolean or an eid
rvid=None, # vid to be applied to other side of rtype (non final relations only)
default_value=None,
- formid=None
+ formid='base'
):
"""display field to edit entity's `rtype` relation on click"""
assert rtype
assert role in ('subject', 'object'), '%s is not an acceptable role value' % role
- if self.__regid__ == 'doreledit':
- assert formid
- self._cw.add_js('cubicweb.reledit.js')
- if formid:
- self._cw.add_js('cubicweb.edition.js')
self._cw.add_css('cubicweb.form.css')
+ self._cw.add_js('cubicweb.reledit.js', 'cubicweb.edition.js')
entity = self.cw_rset.get_entity(row, col)
rschema = self._cw.vreg.schema[rtype]
+ self._rules = rctrl.etype_get(entity.e_schema.type, rschema.type, role, '*')
reload = self._compute_reload(entity, rschema, role, reload)
default_value = self._compute_default_value(entity, rschema, role, default_value)
divid = self._build_divid(rtype, role, entity.eid)
if rschema.final:
- self._handle_attributes(entity, rschema, role, divid, reload, default_value)
+ self._handle_attribute(entity, rschema, role, divid, reload, default_value)
else:
- self._handle_relations(entity, rschema, role, divid, reload, default_value, formid)
+ if self._is_composite():
+ self._handle_composite(entity, rschema, role, divid, reload, default_value, formid)
+ else:
+ self._handle_relation(entity, rschema, role, divid, reload, default_value, formid)
- def _handle_attributes(self, entity, rschema, role, divid, reload, default_value):
+ def _handle_attribute(self, entity, rschema, role, divid, reload, default_value):
rtype = rschema.type
value = entity.printable_value(rtype)
- form, renderer = self._build_form(entity, rtype, role, divid, 'base',
- default_value, reload)
- if not self._should_edit_attribute(entity, rschema, form):
+ if not self._should_edit_attribute(entity, rschema):
self.w(value)
return
+
+ display_label, related_entity = self._prepare_form(entity, rtype, role)
+ form, renderer = self._build_form(entity, rtype, role, divid, 'base', default_value,
+ reload, display_label, related_entity)
value = value or default_value
- field = form.field_by_name(rtype, role, entity.e_schema)
- form.append_field(field)
self.view_form(divid, value, form, renderer)
- def _handle_relations(self, entity, rschema, role, divid, reload, default_value, formid):
- rtype = rschema.type
- rvid = self._compute_best_vid(entity.e_schema, rschema, role)
- related_rset = entity.related(rtype, role)
+ def _compute_formid_value(self, entity, rschema, role, default_value, rvid, formid):
+ related_rset = entity.related(rschema.type, role)
if related_rset:
value = self._cw.view(rvid, related_rset)
else:
value = default_value
- ttypes = self._compute_ttypes(rschema, role)
+ if not self._should_edit_relation(entity, rschema, role):
+ return None, value
+ return formid, value
+
+ def _handle_relation(self, entity, rschema, role, divid, reload, default_value, formid):
+ rvid = self._compute_best_vid(entity.e_schema, rschema, role)
+ formid, value = self._compute_formid_value(entity, rschema, role, default_value, rvid, formid)
+ if formid is None:
+ return self.w(value)
- if not self._should_edit_relation(entity, rschema, role):
- self.w(value)
- return
- # this is for attribute-like composites (1 target type, 1 related entity at most)
+ rtype = rschema.type
+ display_label, related_entity = self._prepare_form(entity, rtype, role)
+ form, renderer = self._build_form(entity, rtype, role, divid, formid, default_value, reload,
+ display_label, related_entity, dict(vid=rvid))
+ self.view_form(divid, value, form, renderer)
+
+ def _handle_composite(self, entity, rschema, role, divid, reload, default_value, formid):
+ # this is for attribute-like composites (1 target type, 1 related entity at most, for now)
+ ttypes = self._compute_ttypes(rschema, role)
+ related_rset = entity.related(rschema.type, role)
add_related = self._may_add_related(related_rset, entity, rschema, role, ttypes)
edit_related = self._may_edit_related_entity(related_rset, entity, rschema, role, ttypes)
delete_related = edit_related and self._may_delete_related(related_rset, entity, rschema, role)
- # compute formid
- if len(ttypes) > 1: # redundant safety belt
- formid = 'base'
- else:
- afs = uicfg.autoform_section.etype_get(entity.e_schema, rschema, role, ttypes[0])
- # is there an afs spec that says we should edit
- # the rschema as an attribute ?
- if afs and 'main_attributes' in afs:
- formid = 'base'
+
+ rvid = self._compute_best_vid(entity.e_schema, rschema, role)
+ formid, value = self._compute_formid_value(entity, rschema, role, default_value, rvid, formid)
+ if formid is None or not (edit_related or add_related):
+ # till we learn to handle cases where not (edit_related or add_related)
+ self.w(value)
+ return
- form, renderer = self._build_form(entity, rtype, role, divid, formid, default_value,
- reload, dict(vid=rvid),
- edit_related, add_related and ttypes[0])
- if formid == 'base':
- field = form.field_by_name(rtype, role, entity.e_schema)
- form.append_field(field)
- self.view_form(divid, value, form, renderer, edit_related,
- delete_related, add_related)
-
+ rtype = rschema.type
+ ttype = ttypes[0]
+ _fdata = self._prepare_composite_form(entity, rtype, role, edit_related, add_related and ttype)
+ display_label, related_entity = _fdata
+ form, renderer = self._build_form(entity, rtype, role, divid, formid, default_value, reload,
+ display_label, related_entity, dict(vid=rvid))
+ self.view_form(divid, value, form, renderer,
+ edit_related, add_related, delete_related)
def _compute_best_vid(self, eschema, rschema, role):
+ rvid = self._one_rvid
if eschema.rdef(rschema, role).role_cardinality(role) in '+*':
- return self._many_rvid
- return self._one_rvid
+ rvid = self._many_rvid
+ return self._rules.get('rvid', rvid)
def _compute_ttypes(self, rschema, role):
dual_role = neg_role(role)
return getattr(rschema, '%ss' % dual_role)()
def _compute_reload(self, entity, rschema, role, reload):
- rule = uicfg.reledit_ctrl.etype_get(entity.e_schema.type, rschema.type, role, '*')
- ctrl_reload = rule.get('reload', reload)
+ ctrl_reload = self._rules.get('reload', reload)
if callable(ctrl_reload):
ctrl_reload = ctrl_reload(entity)
if isinstance(ctrl_reload, int) and ctrl_reload > 1: # not True/False
@@ -160,8 +173,7 @@
def _compute_default_value(self, entity, rschema, role, default_value):
etype = entity.e_schema.type
- rule = uicfg.reledit_ctrl.etype_get(etype, rschema.type, role, '*')
- ctrl_default = rule.get('default_value', default_value)
+ ctrl_default = self._rules.get('default_value', default_value)
if ctrl_default:
return ctrl_default
if default_value is None:
@@ -169,47 +181,42 @@
display_name(self._cw, rschema.type, role))
return default_value
- def _is_composite(self, eschema, rschema, role):
- return eschema.rdef(rschema, role).composite == role
+ def _is_composite(self):
+ return self._rules.get('edit_target') == 'related'
def _may_add_related(self, related_rset, entity, rschema, role, ttypes):
""" ok for attribute-like composite entities """
- if self._is_composite(entity.e_schema, rschema, role):
- if len(ttypes) > 1: # wrong cardinality: do not handle
- return False
- rdef = rschema.role_rdef(entity.e_schema, ttypes[0], role)
- card = rdef.role_cardinality(role)
- if related_rset and card in '?1':
- return False
- if role == 'subject':
- kwargs = {'fromeid': entity.eid}
- else:
- kwargs = {'toeid': entity.eid}
- if rdef.has_perm(self._cw, 'add', **kwargs):
- return True
- return False
+ if len(ttypes) > 1: # many etypes: learn how to do it
+ return False
+ rdef = rschema.role_rdef(entity.e_schema, ttypes[0], role)
+ card = rdef.role_cardinality(role)
+ if related_rset or card not in '?1':
+ return False
+ if role == 'subject':
+ kwargs = {'fromeid': entity.eid}
+ else:
+ kwargs = {'toeid': entity.eid}
+ return rdef.has_perm(self._cw, 'add', **kwargs)
def _may_edit_related_entity(self, related_rset, entity, rschema, role, ttypes):
""" controls the edition of the related entity """
- if entity.e_schema.rdef(rschema, role).role_cardinality(role) not in '?1':
- return False
- if len(related_rset.rows) != 1:
+ if len(ttypes) > 1 or len(related_rset.rows) != 1:
return False
- if len(ttypes) > 1:
- return False
- if not self._is_composite(entity.e_schema, rschema, role):
+ if entity.e_schema.rdef(rschema, role).role_cardinality(role) not in '?1':
return False
return related_rset.get_entity(0, 0).cw_has_perm('update')
def _may_delete_related(self, related_rset, entity, rschema, role):
- # we assume may_edit_related
- kwargs = {'fromeid': entity.eid} if role == 'subject' else {'toeid': entity.eid}
- if not rschema.has_perm(self._cw, 'delete', **kwargs):
+ # we assume may_edit_related, only 1 related entity
+ if not related_rset:
return False
- for related_entity in related_rset.entities():
- if not related_entity.cw_has_perm('delete'):
- return False
- return True
+ rentity = related_rset.get_entity(0, 0)
+ if role == 'subject':
+ kwargs = {'fromeid': entity.eid, 'toeid': rentity.eid}
+ else:
+ kwargs = {'fromeid': rentity.eid, 'toeid': entity.eid}
+ # NOTE: should be sufficient given a well built schema/security
+ return rschema.has_perm(self._cw, 'delete', **kwargs)
def _build_edit_zone(self):
return self._editzone % {'msg' : xml_escape(_(self._cw._(self._editzonemsg)))}
@@ -234,32 +241,43 @@
event_args.update(extradata)
return event_args
- def _build_form(self, entity, rtype, role, divid, formid, default_value, reload,
- extradata=None, edit_related=False, add_related=False, **formargs):
- event_args = self._build_args(entity, rtype, role, formid, default_value,
- reload, extradata)
- cancelclick = self._cancelclick % divid
+ def _prepare_form(self, entity, _rtype, role):
+ display_label = False
+ related_entity = entity
+ return display_label, related_entity
+
+ def _prepare_composite_form(self, entity, rtype, role, edit_related, add_related):
if edit_related and not add_related:
- display_fields = None
display_label = True
related_entity = entity.related(rtype, role).get_entity(0, 0)
- self._cw.form['eid'] = related_entity.eid
elif add_related:
- display_fields = None
display_label = True
_new_entity = self._cw.vreg['etypes'].etype_class(add_related)(self._cw)
_new_entity.eid = self._cw.varmaker.next()
related_entity = _new_entity
+ # XXX see forms.py ~ 276 and entities.linked_to method
+ # is there another way ?
self._cw.form['__linkto'] = '%s:%s:%s' % (rtype, entity.eid, neg_role(role))
- else: # base case: edition/attribute relation
- display_fields = [(rtype, role)]
- display_label = False
- related_entity = entity
+ return display_label, related_entity
+
+ def _build_renderer(self, related_entity, display_label):
+ return self._cw.vreg['formrenderers'].select(
+ self._form_renderer_id, self._cw, entity=related_entity,
+ display_label=display_label,
+ table_class='attributeForm' if display_label else '',
+ display_help=False, button_bar_class='buttonbar',
+ display_progress_div=False)
+
+ def _build_form(self, entity, rtype, role, divid, formid, default_value, reload,
+ display_label, related_entity, extradata=None, **formargs):
+ event_args = self._build_args(entity, rtype, role, formid, default_value,
+ reload, extradata)
+ cancelclick = self._cancelclick % divid
form = self._cw.vreg['forms'].select(
- formid, self._cw, rset=related_entity.as_rset(), entity=related_entity, domid='%s-form' % divid,
- display_fields=display_fields, formtype='inlined',
- action=self._cw.build_url('validateform?__onsuccess=window.parent.cw.reledit.onSuccess'),
- cwtarget='eformframe', cssstyle='display: none',
+ formid, self._cw, rset=related_entity.as_rset(), entity=related_entity,
+ domid='%s-form' % divid, formtype='inlined',
+ action=self._cw.build_url('validateform', __onsuccess='window.parent.cw.reledit.onSuccess'),
+ cwtarget='eformframe', cssclass='releditForm',
**formargs)
# pass reledit arguments
for pname, pvalue in event_args.iteritems():
@@ -279,53 +297,38 @@
form.form_buttons = [SubmitButton(),
Button(stdmsgs.BUTTON_CANCEL, onclick=cancelclick)]
form.event_args = event_args
- renderer = self._cw.vreg['formrenderers'].select(
- 'base', self._cw, entity=related_entity, display_label=display_label,
- display_help=False, table_class='',
- button_bar_class='buttonbar', display_progress_div=False)
- return form, renderer
+ if formid == 'base':
+ field = form.field_by_name(rtype, role, entity.e_schema)
+ form.append_field(field)
+ return form, self._build_renderer(related_entity, display_label)
- def _should_edit_attribute(self, entity, rschema, form):
- # examine rtags
- noedit = uicfg.reledit_ctrl.etype_get(entity.e_schema, rschema.type, 'subject').get('noedit', False)
- if noedit:
- return False
+ def _should_edit_attribute(self, entity, rschema):
rdef = entity.e_schema.rdef(rschema)
- afs = uicfg.autoform_section.etype_get(entity.__regid__, rschema, 'subject', rdef.object)
- if 'main_hidden' in afs:
- return False
# check permissions
if not entity.cw_has_perm('update'):
return False
rdef = entity.e_schema.rdef(rschema)
- if not rdef.has_perm(self._cw, 'update', eid=entity.eid):
- return False
- # XXX ?
- try:
- form.field_by_name(str(rschema), 'subject', entity.e_schema)
- except FieldNotFound:
- return False
- return True
+ return rdef.has_perm(self._cw, 'update', eid=entity.eid)
+
+ should_edit_attributes = deprecated('[3.9] should_edit_attributes is deprecated,'
+ ' use _should_edit_attribute instead',
+ _should_edit_attribute)
def _should_edit_relation(self, entity, rschema, role):
# examine rtags
rtype = rschema.type
- noedit = uicfg.reledit_ctrl.etype_get(entity.e_schema, rtype, role).get('noedit', False)
- if noedit:
- return False
rdef = entity.e_schema.rdef(rschema, role)
- afs = uicfg.autoform_section.etype_get(
- entity.__regid__, rschema, role, rdef.object)
- if 'main_hidden' in afs:
- return False
perm_args = {'fromeid': entity.eid} if role == 'subject' else {'toeid': entity.eid}
return rschema.has_perm(self._cw, 'add', **perm_args)
- def view_form(self, divid, value, form=None, renderer=None,
- edit_related=False, delete_related=False, add_related=False):
+ should_edit_relations = deprecated('[3.9] should_edit_relations is deprecated,'
+ ' use _should_edit_relation instead',
+ _should_edit_relation)
+
+ def _open_form_wrapper(self, divid, value, form, renderer):
w = self.w
- w(u'<div id="%(id)s-reledit" onmouseout="%(out)s" onmouseover="%(over)s">' %
- {'id': divid,
+ w(u'<div id="%(id)s-reledit" onmouseout="%(out)s" onmouseover="%(over)s" class="%(css)s">' %
+ {'id': divid, 'css': 'releditField',
'out': "jQuery('#%s').addClass('hidden')" % divid,
'over': "jQuery('#%s').removeClass('hidden')" % divid})
w(u'<div id="%s-value" class="editableFieldValue">' % divid)
@@ -333,15 +336,24 @@
w(u'</div>')
w(form.render(renderer=renderer))
w(u'<div id="%s" class="editableField hidden">' % divid)
+
+ def _close_form_wrapper(self):
+ self.w(u'</div>')
+ self.w(u'</div>')
+
+ def view_form(self, divid, value, form=None, renderer=None,
+ edit_related=False, add_related=False, delete_related=False):
+ self._open_form_wrapper(divid, value, form, renderer)
+ w = self.w
args = form.event_args.copy()
- if not add_related: # excludes edition
- args['formid'] = 'edition'
+ if not add_related: # currently, excludes edition
+ args['formid'] = 'edition' if edit_related else 'base'
w(u'<div id="%s-update" class="editableField" onclick="%s" title="%s">' %
(divid, xml_escape(self._onclick % args), self._cw._(self._editzonemsg)))
w(self._build_edit_zone())
w(u'</div>')
else:
- args['formid'] = 'edition'
+ args['formid'] = 'edition' if add_related else 'base'
w(u'<div id="%s-add" class="editableField" onclick="%s" title="%s">' %
(divid, xml_escape(self._onclick % args), self._cw._(self._addmsg)))
w(self._build_add_zone())
@@ -352,14 +364,14 @@
(divid, xml_escape(self._onclick % args), self._cw._(self._deletemsg)))
w(self._build_delete_zone())
w(u'</div>')
- w(u'</div>')
- w(u'</div>')
+ self._close_form_wrapper()
+
class AutoClickAndEditFormView(ClickAndEditFormView):
__regid__ = 'reledit'
def _build_form(self, entity, rtype, role, divid, formid, default_value, reload,
- extradata=None, edit_related=False, add_related=False, **formargs):
+ display_label, related_entity, extradata=None, **formargs):
event_args = self._build_args(entity, rtype, role, 'base', default_value,
reload, extradata)
form = _DummyForm()