Override autoform and ajax function retrieving inline creation form to propagate container's eid
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 13 Oct 2016 22:27:17 +0200
changeset 1839 cc3540bc2628
parent 1838 ee6b7e0d1c52
child 1840 e183d3e35d7a
Override autoform and ajax function retrieving inline creation form to propagate container's eid Monkey-patch autoform to add an hidden input on the main form (we can't use 'formtype' since it's not appropriate (may be either 'main' or 'inlined'), rather check if 'peid' is in kwargs to detect if this is the root form or a sub form. Then, propagate value found there as a form param when calling the ajax function to render an inline creation form. Overriding is not necessary for edition form, since in that case we already have the information. Doing so allows to simplify the parent_and_container implementation, and some place are we can now expect the container to be always detectable.
data/cubes.seda.form.js
entities/__init__.py
i18n/en.po
i18n/fr.po
test/test_entities.py
views/dataobject.py
views/mgmt_rules.py
views/patches.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/cubes.seda.form.js	Thu Oct 13 22:27:17 2016 +0200
@@ -0,0 +1,33 @@
+// overwrite CW's addInlineCreationForm function to propagate container eid information
+function addInlineCreationForm(peid, petype, ttype, rtype, role, i18nctx, insertBefore) {
+    insertBefore = insertBefore || cw.getNode('add' + rtype + ':' + peid + 'link').parentNode;
+    var ceid = $('#sedaContainerEID');
+    if (ceid.length == 1) {
+        form = {'sedaContainerEID': ceid.attr('value')}
+    } else {
+        form = null;
+    }
+    var args = ajaxFuncArgs('inline_creation_form', form, peid, petype, ttype, rtype, role, i18nctx);
+    var d = loadRemote(AJAX_BASE_URL, args);
+    d.addCallback(function(response) {
+        var dom = getDomFromResponse(response);
+        loadAjaxHtmlHead(dom);
+        var form = jQuery(dom);
+        form.css('display', 'none');
+        form.insertBefore(insertBefore).slideDown('fast');
+        updateInlinedEntitiesCounters(rtype, role);
+        reorderTabindex(null, $(insertBefore).closest('form')[0]);
+        jQuery(cw).trigger('inlinedform-added', form);
+        // if the inlined form contains a file input, we must force
+        // the form enctype to multipart/form-data
+        if (form.find('input:file').length) {
+            // NOTE: IE doesn't support dynamic enctype modification, we have
+            //       to set encoding too.
+            form.closest('form').attr('enctype', 'multipart/form-data').attr('encoding', 'multipart/form-data');
+        }
+        _postAjaxLoad(dom);
+    });
+    d.addErrback(function(xxx) {
+        cw.log('xxx =', xxx);
+    });
+}
--- a/entities/__init__.py	Thu Oct 13 22:00:55 2016 +0200
+++ b/entities/__init__.py	Thu Oct 13 22:27:17 2016 +0200
@@ -48,24 +48,10 @@
             # ajax created form
             try:
                 parent_eid = int(json.loads(req.form['arg'][0]))
-            except KeyError:
-                # direct inlined form (not through ajax) - eid may not be the direct parent, but
-                # this is enough to get the container
-                try:
-                    eid = int(req.form['eid'])
-                except KeyError:
-                    # unable to get parent eid for now :(
-                    return None, None
-                else:
-                    ancestor = req.entity_from_eid(eid)
-                    icontainer = ancestor.cw_adapt_to('IContainer')
-                    if icontainer is None:
-                        container = ancestor.cw_adapt_to('IContained').container
-                    else:
-                        container = icontainer.container
-                    # unable to get parent eid for now :(
+            except (KeyError, ValueError):
+                if 'sedaContainerEID' in req.form:
+                    container = req.entity_from_eid(int(req.form['sedaContainerEID']))
                     return None, container
-            except ValueError:
                 # unable to get parent eid for now :(
                 return None, None
         parent = req.entity_from_eid(parent_eid)
--- a/i18n/en.po	Thu Oct 13 22:00:55 2016 +0200
+++ b/i18n/en.po	Thu Oct 13 22:27:17 2016 +0200
@@ -8577,6 +8577,3 @@
 #, python-brace-format
 msgid "you must specify a scheme for {0} to select a value"
 msgstr ""
-
-msgid "you must validate first to select a possible value"
-msgstr ""
--- a/i18n/fr.po	Thu Oct 13 22:00:55 2016 +0200
+++ b/i18n/fr.po	Thu Oct 13 22:27:17 2016 +0200
@@ -8598,5 +8598,3 @@
 msgid "you must specify a scheme for {0} to select a value"
 msgstr "spécifiez d'abord un vocabulaire pour \"{0}\""
 
-msgid "you must validate first to select a possible value"
-msgstr "validez avant de pouvoir sélectionner une valeur"
--- a/test/test_entities.py	Thu Oct 13 22:00:55 2016 +0200
+++ b/test/test_entities.py	Thu Oct 13 22:27:17 2016 +0200
@@ -195,7 +195,7 @@
     def test_eid(self):
         with self.admin_access.web_request() as req:
             transfer = req.cnx.create_entity('SEDAArchiveTransfer', title=u'test profile')
-            req.form['eid'] = text_type(transfer.eid)
+            req.form['sedaContainerEID'] = text_type(transfer.eid)
             parent, container = parent_and_container(attrdict(_cw=req, has_eid=lambda: False))
             self.assertIsNone(parent)
             self.assertEqual(container.eid, transfer.eid)
--- a/views/dataobject.py	Thu Oct 13 22:00:55 2016 +0200
+++ b/views/dataobject.py	Thu Oct 13 22:27:17 2016 +0200
@@ -46,28 +46,26 @@
 
     def _render_triggers(self, w, domid, form, field, rtype):
         parent, container = parent_and_container(form.edited_entity)
+        assert container is not None
         req = form._cw
-        if container is None:
-            w(req._('you must validate first to select a possible value'))
+        try:
+            constraint = RDEF_CONSTRAINTS[(form.edited_entity.cw_etype, field.name)]
+        except KeyError:
+            constraint = RDEF_CONSTRAINTS[field.name]
+        rql = 'Any CS WHERE ' + (
+            constraint.replace('O in_scheme CS, ', '').replace(', S container AT', ''))
+        rql += ', AT eid %(at)s'
+        if not req.execute(rql, {'at': container.eid}):
+            scheme_relations = [x for x in rql.split() if x.startswith('seda_')]
+            if len(scheme_relations) == 1:
+                scheme_relation = req._(scheme_relations[0])
+            else:
+                scheme_relation = req.__(scheme_relations[0] + '_object')
+            w(req._('you must specify a scheme for {0} to select a value').format(
+                scheme_relation))
         else:
-            try:
-                constraint = RDEF_CONSTRAINTS[(form.edited_entity.cw_etype, field.name)]
-            except KeyError:
-                constraint = RDEF_CONSTRAINTS[field.name]
-            rql = 'Any CS WHERE ' + (
-                constraint.replace('O in_scheme CS, ', '').replace(', S container AT', ''))
-            rql += ', AT eid %(at)s'
-            if not req.execute(rql, {'at': container.eid}):
-                scheme_relations = [x for x in rql.split() if x.startswith('seda_')]
-                if len(scheme_relations) == 1:
-                    scheme_relation = req._(scheme_relations[0])
-                else:
-                    scheme_relation = req.__(scheme_relations[0] + '_object')
-                w(req._('you must specify a scheme for {0} to select a value').format(
-                    scheme_relation))
-            else:
-                return super(ContainedRelationFacetWidget, self)._render_triggers(
-                    w, domid, form, field, rtype)
+            return super(ContainedRelationFacetWidget, self)._render_triggers(
+                w, domid, form, field, rtype)
 
     def trigger_search_url(self, entity, url_params):
         """Overriden to add information about who is the container
--- a/views/mgmt_rules.py	Thu Oct 13 22:00:55 2016 +0200
+++ b/views/mgmt_rules.py	Thu Oct 13 22:27:17 2016 +0200
@@ -39,10 +39,7 @@
     """
     req = form._cw
     parent, container = parent_and_container(form.edited_entity)
-    if container is None:
-        # missing parent information
-        msg = req._('you must validate first to select a possible value')
-        return [(msg, INTERNAL_FIELD_VALUE)]
+    assert container is not None
     parent_etype = form.edited_entity.cw_etype
     if parent_etype == 'SEDARefNonRuleId':
         if parent is not None:
--- a/views/patches.py	Thu Oct 13 22:00:55 2016 +0200
+++ b/views/patches.py	Thu Oct 13 22:27:17 2016 +0200
@@ -17,6 +17,36 @@
 
 from logilab.common.decorators import monkeypatch
 
+from cubicweb.web.views import autoform  # noqa
+
+
+# monkey patch autoform to add an hidden field container the parent container eid that may be used
+# in parent_and_container (see entities/__init__.py)
+
+orig_autoform_init = autoform.AutomaticEntityForm.__init__
+
+
+@monkeypatch(autoform.AutomaticEntityForm)
+def __init__(self, *args, **kwargs):
+    orig_autoform_init(self, *args, **kwargs)
+    if 'peid' not in kwargs:  # main form
+        parent = None
+        if self.edited_entity.has_eid():
+            parent = self.edited_entity
+        elif '__linkto' in self._cw.form:
+            parent = self._cw.entity_from_eid(int(self._cw.form['__linkto'].split(':')[1]))
+        if parent is not None:
+            if parent.cw_adapt_to('IContainer') is not None:
+                container = parent
+            else:
+                container = parent.cw_adapt_to('IContained').container
+            self.add_hidden(name='sedaContainerEID', value=container.eid, id='sedaContainerEID')
+
+
+# this js file contains a custom implementation of addInlineCreationForm that propage
+# sedaContainerEID
+autoform.AutomaticEntityForm.needs_js += ('cubes.seda.form.js',)
+
 
 # fix unexpected redirect after clicking on cancel in reledit ######################################
 # (https://www.cubicweb.org/ticket/13120795)
@@ -82,7 +112,6 @@
 # (https://www.cubicweb.org/ticket/15755515)
 
 from logilab.common.decorators import cached  # noqa
-from cubicweb.web.views import autoform  # noqa
 
 autoform.InlineEntityEditionFormView.form_renderer_id = 'inline'