[reledit] kill the reledit/doreledit duality
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Wed, 19 Jan 2011 19:19:57 +0100
changeset 7076 0eed6045d785
parent 7070 5f8e52d722c5
child 7077 784d6f300070
[reledit] kill the reledit/doreledit duality * * * [reledit] use action to determine form content * * * [reledit] use self.entity instead of passing entity everywhere
web/data/cubicweb.reledit.js
web/views/basecontrollers.py
web/views/reledit.py
--- a/web/data/cubicweb.reledit.js	Thu Mar 10 12:04:46 2011 +0100
+++ b/web/data/cubicweb.reledit.js	Wed Jan 19 19:19:57 2011 +0100
@@ -18,6 +18,7 @@
     cleanupAfterCancel: function (divid) {
         jQuery('#appMsg').hide();
         jQuery('div.errorMessage').remove();
+        // plus re-set inline style ?
         jQuery('#' + divid).show();
         jQuery('#' + divid + '-value').show();
         jQuery('#' + divid + '-form').hide();
@@ -63,9 +64,9 @@
      * @param reload: boolean to reload page if true (when changing URL dependant data)
      * @param default_value : value if the field is empty
      */
-    loadInlineEditionForm: function(formid, eid, rtype, role, divid, reload, vid) {
+    loadInlineEditionForm: function(formid, eid, rtype, role, divid, reload, vid, action) {
         var args = {fname: 'reledit_form', rtype: rtype, role: role,
-                    pageid: pageid,
+                    pageid: pageid, action: action,
                     eid: eid, divid: divid, formid: formid,
                     reload: reload, vid: vid};
         var d = jQuery('#'+divid+'-reledit').loadxhtml(JSON_BASE_URL, args, 'post');
--- a/web/views/basecontrollers.py	Thu Mar 10 12:04:46 2011 +0100
+++ b/web/views/basecontrollers.py	Wed Jan 19 19:19:57 2011 +0100
@@ -455,13 +455,13 @@
     def js_reledit_form(self):
         req = self._cw
         args = dict((x, req.form[x])
-                    for x in ('formid', 'rtype', 'role', 'reload'))
+                    for x in ('formid', 'rtype', 'role', 'reload', 'action'))
         rset = req.eid_rset(typed_eid(self._cw.form['eid']))
         try:
             args['reload'] = json.loads(args['reload'])
         except ValueError: # not true/false, an absolute url
             assert args['reload'].startswith('http')
-        view = req.vreg['views'].select('doreledit', req, rset=rset, rtype=args['rtype'])
+        view = req.vreg['views'].select('reledit', req, rset=rset, rtype=args['rtype'])
         return self._call_view(view, **args)
 
     @jsonize
--- 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)