[web/views/formrenderer] do not use `cubicweb:target` attribute on form (closes #5534074)
authorAlain Leufroy <alain.leufroy@logilab.fr>
Wed, 20 May 2015 16:13:07 +0200
changeset 10434 8e04ab5582d9
parent 10433 523a7f098066
child 10435 5b33ca2c61e0
[web/views/formrenderer] do not use `cubicweb:target` attribute on form (closes #5534074) The `cubicweb:target` is a flag indicating that the form data (with file input) shall be posted inside an iframe - this is a well known "ajax-like" workaround to post files with browsers that do not support `FormData` (a.k.a IE<10). The `cubicweb:target` was introduced when CW used the "xhtml strict" doctype. Now that CW uses the "html5" doctype, this namespaced attribute is no longer necessary and the iframe can be generated directly. Before this patch, CW inserts the `cubicweb:target` attribute in the form DOM element (server side) and `setFormsTarget()` updates the DOM with a new `<iframe>` element (client side). Now, CW inserts the `<iframe>` DOM element directly (server side), making `setFormsTarget` useless.
doc/book/en/devweb/edition/dissection.rst
web/data/cubicweb.ajax.js
web/data/cubicweb.edition.js
web/test/unittest_reledit.py
web/views/formrenderers.py
web/views/forms.py
--- a/doc/book/en/devweb/edition/dissection.rst	Fri Jul 25 17:10:05 2014 +0200
+++ b/doc/book/en/devweb/edition/dissection.rst	Wed May 20 16:13:07 2015 +0200
@@ -60,7 +60,7 @@
  <div class="formBody">
   <form action="http://crater:9999/validateform" method="post" enctype="application/x-www-form-urlencoded"
         id="entityForm" onsubmit="return freezeFormButtons(&#39;entityForm&#39;);"
-        class="entityForm" cubicweb:target="eformframe">
+        class="entityForm" target="eformframe">
     <div id="progress">validating...</div>
     <fieldset>
       <input name="__form_id" type="hidden" value="edition" />
@@ -73,6 +73,7 @@
              value="concerns-subject,done_in-subject,priority-subject,type-subject,title-subject,description-subject,__type,_cw_generic_field" />
       ...
     </fieldset>
+    <iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0);"></iframe>
    </form>
  </div>
 
@@ -259,36 +260,13 @@
 The form validation process
 ---------------------------
 
-Preparation
-~~~~~~~~~~~
-
-After the (html) document is loaded, the ``setFormsTarget`` javascript
-function dynamically transforms the DOM as follows. For all forms of
-the DOM, it:
-
-* sets the ``target`` attribute where there is a ``cubicweb:target``
-  attribute (with the same value)
-
-* appends an empty `IFRAME` element at the end
-
-Let us have a look again at the form element. We have omitted some
-irrelevant attributes.
-
-.. sourcecode::html
-
-  <form action="http://crater:9999/validateform" method="post"
-        enctype="application/x-www-form-urlencoded"
-        id="entityForm" cubicweb:target="eformframe"
-        target="eformframe">
-  ...
-  </form>
-
 Validation loop
 ~~~~~~~~~~~~~~~
 
 On form submission, the form.action is invoked. Basically, the
 ``validateform`` controller is called and its output lands in the
-specified ``target``, the iframe that was previously prepared.
+specified ``target``, an invisible ``<iframe>`` at the end of the
+form.
 
 Hence, the main page is not replaced, only the iframe contents. The
 ``validateform`` controller only outputs a tiny javascript fragment
@@ -296,7 +274,7 @@
 
 .. sourcecode:: html
 
- <iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0)">
+ <iframe width="0px" height="0px" name="eformframe" id="eformframe" src="javascript: void(0);">
    <script type="text/javascript">
      window.parent.handleFormValidationResponse('entityForm', null, null,
                                                 [false, [2164, {"name-subject": "required field"}], null],
--- a/web/data/cubicweb.ajax.js	Fri Jul 25 17:10:05 2014 +0200
+++ b/web/data/cubicweb.ajax.js	Wed May 20 16:13:07 2015 +0200
@@ -275,9 +275,6 @@
     if (typeof roundedCorners != 'undefined') {
         roundedCorners(node);
     }
-    if (typeof setFormsTarget != 'undefined') {
-        setFormsTarget(node);
-    }
     _loadDynamicFragments(node);
     jQuery(cw).trigger('server-response', [true, node]);
     jQuery(node).trigger('server-response', [true, node]);
--- a/web/data/cubicweb.edition.js	Fri Jul 25 17:10:05 2014 +0200
+++ b/web/data/cubicweb.edition.js	Wed May 20 16:13:07 2015 +0200
@@ -537,53 +537,6 @@
 }
 
 /**
- * .. function:: setFormsTarget(node)
- *
- * called on load to set target and iframeso object.
- *
- * .. note::
- *
- *    This was a hack to make form loop handling XHTML compliant.
- *    Since we do not care about xhtml any longer, this may go away.
- *
- * .. note::
- *
- *   `object` nodes might be a potential replacement for iframes
- *
- * .. note::
- *
- *    The form's `target` attribute should probably become a simple data-target
- *    immediately generated server-side.
- *    Since we don't do xhtml any longer, the iframe should probably be either
- *    reconsidered or at least emitted server-side.
- */
-function setFormsTarget(node) {
-    var $node = jQuery(node || document.body);
-    $node.find('form').each(function() {
-        var form = jQuery(this);
-        var target = form.attr('cubicweb:target');
-        if (target) {
-            form.attr('target', target);
-            /* do not use display: none because some browsers ignore iframe
-             * with no display */
-            form.append(IFRAME({
-                name: target,
-                id: target,
-                src: 'javascript: void(0)',
-                width: '0px',
-                height: '0px'
-            }));
-            form.removeAttr('cubicweb:target'); // useles from now on, pop it
-                                                // to make IE9 happy
-        }
-    });
-}
-
-jQuery(document).ready(function() {
-    setFormsTarget();
-});
-
-/**
  * .. function:: validateForm(formid, action, onsuccess, onfailure)
  *
  * called on traditionnal form submission : the idea is to try
--- a/web/test/unittest_reledit.py	Fri Jul 25 17:10:05 2014 +0200
+++ b/web/test/unittest_reledit.py	Wed May 20 16:13:07 2015 +0200
@@ -53,7 +53,7 @@
 
     def test_default_forms(self):
         self.skipTest('Need to check if this test should still run post reledit/doreledit merge')
-        doreledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="title-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;title-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
+        doreledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="title-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;title-subject-%(eid)s-form&#39;);" class="releditForm" target="eformframe">
 <fieldset>
 <input name="__form_id" type="hidden" value="base" />
 <input name="__errorurl" type="hidden" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop#title-subject-%(eid)s-form" />
@@ -82,9 +82,10 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;title-subject-%(eid)s&#39;)" tabindex="3" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
+<iframe width="0px" height="0px" src="javascript: void(0);" name="eformframe" id="eformframe"></iframe>
 </form><div id="title-subject-%(eid)s" class="editableField invisible"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, false, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
 
-                     'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="long_desc-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;long_desc-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
+                     'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="long_desc-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;long_desc-subject-%(eid)s-form&#39;);" class="releditForm" target="eformframe">
 <fieldset>
 <input name="__form_id" type="invisible" value="edition" />
 <input name="__errorurl" type="invisible" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop#long_desc-subject-%(eid)s-form" />
@@ -126,9 +127,10 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;long_desc-subject-%(eid)s&#39;)" tabindex="8" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
+<iframe width="0px" height="0px" src="javascript: void(0);" name="eformframe" id="eformframe"></iframe>
 </form><div id="long_desc-subject-%(eid)s" class="editableField invisible"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
 
-                     'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="manager-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;manager-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
+                     'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('invisible')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('invisible')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="manager-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;manager-subject-%(eid)s-form&#39;);" class="releditForm" target="eformframe">
 <fieldset>
 <input name="__form_id" type="hidden" value="base" />
 <input name="__errorurl" type="hidden" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop#manager-subject-%(eid)s-form" />
@@ -162,6 +164,7 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;manager-subject-%(eid)s&#39;)" tabindex="11" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
+<iframe width="0px" height="0px" src="javascript: void(0);" name="eformframe" id="eformframe"></iframe>
 </form><div id="manager-subject-%(eid)s" class="editableField invisible"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
                      'composite_card11_2ttypes': """&lt;not specified&gt;""",
                      'concerns': """&lt;not specified&gt;"""
--- a/web/views/formrenderers.py	Fri Jul 25 17:10:05 2014 +0200
+++ b/web/views/formrenderers.py	Wed May 20 16:13:07 2015 +0200
@@ -196,7 +196,7 @@
         if form.cssclass:
             attrs.setdefault('class', form.cssclass)
         if form.cwtarget:
-            attrs.setdefault('cubicweb:target', form.cwtarget)
+            attrs.setdefault('target', form.cwtarget)
         if not form.autocomplete:
             attrs.setdefault('autocomplete', 'off')
         return '<form %s>' % uilib.sgml_attributes(attrs)
@@ -206,7 +206,13 @@
         for form renderers overriding open_form to use something else or more than
         and <form>
         """
-        return u'</form>'
+        out = u'</form>'
+        if form.cwtarget:
+            attrs = {'name': form.cwtarget, 'id': form.cwtarget,
+                     'width': '0px', 'height': '0px',
+                     'src': 'javascript: void(0);'}
+            out =  (u'<iframe %s></iframe>\n' % uilib.sgml_attributes(attrs)) + out
+        return out
 
     def render_fields(self, w, form, values):
         fields = self._render_hidden_fields(w, form)
--- a/web/views/forms.py	Fri Jul 25 17:10:05 2014 +0200
+++ b/web/views/forms.py	Wed May 20 16:13:07 2015 +0200
@@ -95,7 +95,7 @@
       value for the "style" attribute of the <form> tag
 
     :attr:`cwtarget`
-      value for the "cubicweb:target" attribute of the <form> tag
+      value for the "target" attribute of the <form> tag
 
     :attr:`redirect_path`
       relative to redirect to after submitting the form