--- a/web/views/reledit.py Thu Mar 10 12:04:46 2011 +0100
+++ b/web/views/reledit.py Wed Jan 19 19:19:57 2011 +0100
@@ -15,7 +15,9 @@
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
-"""the 'reedit' feature (eg edit attribute/relation from primary view"""
+"""edit entity attributes/relations from any view, without going to the entity
+form
+"""
__docformat__ = "restructuredtext en"
_ = unicode
@@ -24,7 +26,8 @@
from warnings import warn
from logilab.mtconverter import xml_escape
-from logilab.common.deprecation import deprecated
+from logilab.common.deprecation import deprecated, class_renamed
+from logilab.common.decorators import cached
from cubicweb import neg_role
from cubicweb.schema import display_name
@@ -43,16 +46,18 @@
return u''
def append_field(self, *args):
pass
+ def add_hidden(self, *args):
+ pass
rctrl = uicfg.reledit_ctrl
-class ClickAndEditFormView(EntityView):
- __regid__ = 'doreledit'
+class AutoClickAndEditFormView(EntityView):
+ __regid__ = 'reledit'
__select__ = non_final_entity() & match_kwargs('rtype')
# ui side continuations
_onclick = (u"cw.reledit.loadInlineEditionForm('%(formid)s', %(eid)s, '%(rtype)s', '%(role)s', "
- "'%(divid)s', %(reload)s, '%(vid)s');")
+ "'%(divid)s', %(reload)s, '%(vid)s', '%(action)s');")
_cancelclick = "cw.reledit.cleanupAfterCancel('%s')"
# ui side actions/buttons
@@ -75,93 +80,84 @@
# 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='base'
+ formid='base',
+ action=None
):
"""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
self._cw.add_css('cubicweb.form.css')
self._cw.add_js(('cubicweb.reledit.js', 'cubicweb.edition.js', 'cubicweb.ajax.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, '*')
+ self._rules = rctrl.etype_get(self.entity.e_schema.type, rschema.type, role, '*')
if rvid is not None or default_value is not None:
warn('[3.9] specifying rvid/default_value on select is deprecated, '
'reledit_ctrl rtag to control this' % self, DeprecationWarning)
- reload = self._compute_reload(entity, rschema, role, reload)
- divid = self._build_divid(rtype, role, entity.eid)
+ reload = self._compute_reload(rschema, role, reload)
+ divid = self._build_divid(rtype, role, self.entity.eid)
if rschema.final:
- self._handle_attribute(entity, rschema, role, divid, reload)
+ self._handle_attribute(rschema, role, divid, reload, action)
else:
if self._is_composite():
- self._handle_composite(entity, rschema, role, divid, reload, formid)
+ self._handle_composite(rschema, role, divid, reload, formid, action)
else:
- self._handle_relation(entity, rschema, role, divid, reload, formid)
+ self._handle_relation(rschema, role, divid, reload, formid, action)
- def _handle_attribute(self, entity, rschema, role, divid, reload):
- rtype = rschema.type
- value = entity.printable_value(rtype)
- if not self._should_edit_attribute(entity, rschema):
+ def _handle_attribute(self, rschema, role, divid, reload, action):
+ value = self.entity.printable_value(rschema.type)
+ if not self._should_edit_attribute(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',
- reload, display_label, related_entity)
+ form, renderer = self._build_form(self.entity, rschema, role, divid, 'base', reload, action)
value = value or self._compute_default_value(rschema, role)
self.view_form(divid, value, form, renderer)
- def _compute_formid_value(self, entity, rschema, role, rvid, formid):
- related_rset = entity.related(rschema.type, role)
+ def _compute_formid_value(self, rschema, role, rvid, formid):
+ related_rset = self.entity.related(rschema.type, role)
if related_rset:
value = self._cw.view(rvid, related_rset)
else:
value = self._compute_default_value(rschema, role)
- if not self._should_edit_relation(entity, rschema, role):
+ if not self._should_edit_relation(rschema, role):
return None, value
return formid, value
- def _handle_relation(self, entity, rschema, role, divid, reload, formid):
+ def _handle_relation(self, rschema, role, divid, reload, formid, action):
rvid = self._rules.get('rvid', 'autolimited')
- formid, value = self._compute_formid_value(entity, rschema, role, rvid, formid)
+ formid, value = self._compute_formid_value(rschema, role, rvid, formid)
if formid is None:
return self.w(value)
- rtype = rschema.type
- display_label, related_entity = self._prepare_form(entity, rtype, role)
- form, renderer = self._build_form(entity, rtype, role, divid, formid, reload,
- display_label, related_entity, dict(vid=rvid))
+ form, renderer = self._build_form(self.entity, rschema, role, divid, formid,
+ reload, action, dict(vid=rvid))
self.view_form(divid, value, form, renderer)
- def _handle_composite(self, entity, rschema, role, divid, reload, formid):
+ def _handle_composite(self, rschema, role, divid, reload, formid, action):
# this is for attribute-like composites (1 target type, 1 related entity at most, for now)
- ttypes = self._compute_ttypes(rschema, role)
+ entity = self.entity
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)
+ add_related = self._may_add_related(related_rset, rschema, role)
+ edit_related = self._may_edit_related_entity(related_rset, rschema, role)
+ delete_related = edit_related and self._may_delete_related(related_rset, rschema, role)
rvid = self._rules.get('rvid', 'autolimited')
- formid, value = self._compute_formid_value(entity, rschema, role, rvid, formid)
+ formid, value = self._compute_formid_value(rschema, role, 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
- 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, reload,
- display_label, related_entity, dict(vid=rvid))
+ form, renderer = self._build_form(entity, rschema, role, divid, formid,
+ reload, action, dict(vid=rvid))
self.view_form(divid, value, form, renderer,
edit_related, add_related, delete_related)
+ @cached
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):
+ def _compute_reload(self, rschema, role, reload):
ctrl_reload = self._rules.get('reload', reload)
if callable(ctrl_reload):
- ctrl_reload = ctrl_reload(entity)
+ ctrl_reload = ctrl_reload(self.entity)
if isinstance(ctrl_reload, int) and ctrl_reload > 1: # not True/False
ctrl_reload = self._cw.build_url(ctrl_reload)
return ctrl_reload
@@ -179,33 +175,36 @@
def _is_composite(self):
return self._rules.get('edit_target') == 'related'
- def _may_add_related(self, related_rset, entity, rschema, role, ttypes):
+ def _may_add_related(self, related_rset, rschema, role):
""" ok for attribute-like composite entities """
+ ttypes = self._compute_ttypes(rschema, role)
if len(ttypes) > 1: # many etypes: learn how to do it
return False
- rdef = rschema.role_rdef(entity.e_schema, ttypes[0], role)
+ rdef = rschema.role_rdef(self.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}
+ kwargs = {'fromeid': self.entity.eid}
else:
- kwargs = {'toeid': entity.eid}
+ kwargs = {'toeid': self.entity.eid}
return rdef.has_perm(self._cw, 'add', **kwargs)
- def _may_edit_related_entity(self, related_rset, entity, rschema, role, ttypes):
+ def _may_edit_related_entity(self, related_rset, rschema, role):
""" controls the edition of the related entity """
+ ttypes = self._compute_ttypes(rschema, role)
if len(ttypes) > 1 or len(related_rset.rows) != 1:
return False
- if entity.e_schema.rdef(rschema, role).role_cardinality(role) not in '?1':
+ if self.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):
+ def _may_delete_related(self, related_rset, rschema, role):
# we assume may_edit_related, only 1 related entity
if not related_rset:
return False
rentity = related_rset.get_entity(0, 0)
+ entity = self.entity
if role == 'subject':
kwargs = {'fromeid': entity.eid, 'toeid': rentity.eid}
else:
@@ -230,33 +229,33 @@
""" builds an id for the root div of a reledit widget """
return '%s-%s-%s' % (rtype, role, entity_eid)
- def _build_args(self, entity, rtype, role, formid, reload,
+ def _build_args(self, entity, rtype, role, formid, reload, action,
extradata=None):
divid = self._build_divid(rtype, role, entity.eid)
event_args = {'divid' : divid, 'eid' : entity.eid, 'rtype' : rtype, 'formid': formid,
- 'reload' : json_dumps(reload),
+ 'reload' : json_dumps(reload), 'action': action,
'role' : role, 'vid' : u''}
if extradata:
event_args.update(extradata)
return event_args
- 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):
- display_label = True
- if edit_related and not add_related:
- related_entity = entity.related(rtype, role).get_entity(0, 0)
- elif add_related:
- _new_entity = self._cw.vreg['etypes'].etype_class(add_related)(self._cw)
+ def _prepare_form(self, entity, rschema, role, action):
+ assert action in ('edit_rtype', 'edit_related', 'add', 'delete'), action
+ if action == 'edit_rtype':
+ return False, entity
+ label = True
+ if action in ('edit_related', 'delete'):
+ edit_entity = entity.related(rschema, role).get_entity(0, 0)
+ elif action == 'add':
+ add_etype = self._compute_ttypes(rschema, role)[0]
+ _new_entity = self._cw.vreg['etypes'].etype_class(add_etype)(self._cw)
_new_entity.eid = self._cw.varmaker.next()
- related_entity = _new_entity
+ edit_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))
- return display_label, related_entity
+ self._cw.form['__linkto'] = '%s:%s:%s' % (rschema, entity.eid, neg_role(role))
+ assert edit_entity
+ return label, edit_entity
def _build_renderer(self, related_entity, display_label):
return self._cw.vreg['formrenderers'].select(
@@ -266,13 +265,18 @@
display_help=False, button_bar_class='buttonbar',
display_progress_div=False)
- def _build_form(self, entity, rtype, role, divid, formid, reload,
- display_label, related_entity, extradata=None, **formargs):
- event_args = self._build_args(entity, rtype, role, formid,
- reload, extradata)
+ def _build_form(self, entity, rschema, role, divid, formid, reload, action,
+ extradata=None, **formargs):
+ rtype = rschema.type
+ event_args = self._build_args(entity, rtype, role, formid, reload, action, extradata)
+ if not action:
+ form = _DummyForm()
+ form.event_args = event_args
+ return form, None
+ label, edit_entity = self._prepare_form(entity, rschema, role, action)
cancelclick = self._cancelclick % divid
form = self._cw.vreg['forms'].select(
- formid, self._cw, rset=related_entity.as_rset(), entity=related_entity,
+ formid, self._cw, rset=edit_entity.as_rset(), entity=edit_entity,
domid='%s-form' % divid, formtype='inlined',
action=self._cw.build_url('validateform', __onsuccess='window.parent.cw.reledit.onSuccess'),
cwtarget='eformframe', cssclass='releditForm',
@@ -298,9 +302,10 @@
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)
+ return form, self._build_renderer(edit_entity, label)
- def _should_edit_attribute(self, entity, rschema):
+ def _should_edit_attribute(self, rschema):
+ entity = self.entity
rdef = entity.e_schema.rdef(rschema)
# check permissions
if not entity.cw_has_perm('update'):
@@ -312,8 +317,8 @@
' use _should_edit_attribute instead',
_should_edit_attribute)
- def _should_edit_relation(self, entity, rschema, role):
- eeid = entity.eid
+ def _should_edit_relation(self, rschema, role):
+ eeid = self.entity.eid
perm_args = {'fromeid': eeid} if role == 'subject' else {'toeid': eeid}
return rschema.has_perm(self._cw, 'add', **perm_args)
@@ -335,9 +340,11 @@
w(u'<div id="%s" class="editableField hidden">' % divid)
def _edit_action(self, divid, args, edit_related, add_related, _delete_related):
+ # XXX disambiguate wrt edit_related
if not add_related: # currently, excludes edition
w = self.w
args['formid'] = 'edition' if edit_related else 'base'
+ args['action'] = 'edit_related' if edit_related else 'edit_rtype'
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())
@@ -346,7 +353,8 @@
def _add_action(self, divid, args, _edit_related, add_related, _delete_related):
if add_related:
w = self.w
- args['formid'] = 'edition' if add_related else 'base'
+ args['formid'] = 'edition'
+ args['action'] = 'add'
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())
@@ -356,6 +364,7 @@
if delete_related:
w = self.w
args['formid'] = 'deleteconf'
+ args['action'] = 'delete'
w(u'<div id="%s-delete" class="editableField" onclick="%s" title="%s">' %
(divid, xml_escape(self._onclick % args), self._cw._(self._deletemsg)))
w(self._build_delete_zone())
@@ -376,13 +385,4 @@
self._close_form_wrapper()
-class AutoClickAndEditFormView(ClickAndEditFormView):
- __regid__ = 'reledit'
-
- def _build_form(self, entity, rtype, role, divid, formid, reload,
- display_label, related_entity, extradata=None, **formargs):
- event_args = self._build_args(entity, rtype, role, 'base',
- reload, extradata)
- form = _DummyForm()
- form.event_args = event_args
- return form, None
+ClickAndEditFormView = class_renamed('ClickAndEditFormView', AutoClickAndEditFormView)