backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 05 May 2010 18:55:19 +0200
changeset 5486 5790462343cb
parent 5483 1a30c5a56256 (current diff)
parent 5485 4a033d7f1d93 (diff)
child 5489 50e2d8e10638
backport stable
doc/book/en/annexes/index.rst
doc/book/en/devweb/js.rst
migration.py
--- a/doc/book/en/annexes/index.rst	Wed May 05 18:48:31 2010 +0200
+++ b/doc/book/en/annexes/index.rst	Wed May 05 18:55:19 2010 +0200
@@ -19,11 +19,3 @@
    depends
    javascript-api
    docstrings-conventions
-
-(X)HTML tricks to apply
------------------------
-
-Some web browser (Firefox for example) are not happy with empty `<div>`
-(by empty we mean that there is no content in the tag, but there
-could be attributes), so we should always use `<div></div>` even if
-it is empty and not use `<div/>`.
--- a/doc/book/en/devweb/edition/dissection.rst	Wed May 05 18:48:31 2010 +0200
+++ b/doc/book/en/devweb/edition/dissection.rst	Wed May 05 18:55:19 2010 +0200
@@ -1,5 +1,7 @@
 
-Dissection of a Form
+.. _form_dissection:
+
+Dissection of a form
 --------------------
 
 This is done (again) with a vanilla instance of the `tracker`_
@@ -110,12 +112,17 @@
 back correctly.
 
 The `freezeFormButtons(...)` javascript callback defined on the
-``conlick`` event of the form element prevents accidental multiple
+``onlick`` event of the form element prevents accidental multiple
 clicks in a row.
 
-The ``action`` of the form is mapped to the `validateform` controller
+The ``action`` of the form is mapped to the ``validateform`` controller
 (situated in :mod:`cubicweb.web.views.basecontrollers`).
 
+A full explanation of the validation loop is given in
+:ref:`validation_process`.
+
+.. _attributes_section:
+
 The attributes section
 ''''''''''''''''''''''
 
@@ -252,19 +259,22 @@
           <tr>
             <td align="center">
               <button class="validateButton" tabindex="9" type="submit" value="validate">
-                <img alt="OK_ICON" src="{}" />validate
+                <img alt="OK_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/ok.png" />
+                validate
               </button>
             </td>
             <td style="align: right; width: 50%;">
               <button class="validateButton"
                       onclick="postForm(&#39;__action_apply&#39;, &#39;button_apply&#39;, &#39;entityForm&#39;)"
                       tabindex="10" type="button" value="apply">
-                <img alt="APPLY_ICON" src="{}" />apply
+                <img alt="APPLY_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/plus.png" />
+                apply
               </button>
               <button class="validateButton"
                       onclick="postForm(&#39;__action_cancel&#39;, &#39;button_cancel&#39;, &#39;entityForm&#39;)"
                       tabindex="11" type="button" value="cancel">
-                <img alt="CANCEL_ICON" src="{}" />cancel
+                <img alt="CANCEL_ICON" src="http://myapp/datafd8b5d92771209ede1018a8d5da46a37/cancel.png" />
+                cancel
               </button>
             </td>
           </tr>
@@ -273,4 +283,87 @@
 
 The most notable artifacts here are the ``postForm(...)`` calls
 defined on click events on these buttons. This function basically
-submits the form. XXX validateform vs validateForm, ajax or not ?
+submits the form.
+
+.. _validation_process:
+
+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.
+
+Hence, the main page is not replaced, only the iframe contents. The
+``validateform`` controller only outputs a tiny javascript fragment
+which is then immediately executed.
+
+.. sourcecode:: html
+
+ <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],
+                                                null);
+   </script>
+ </iframe>
+
+The ``window.parent`` part ensures the javascript function is called
+on the right context (that is: the form element). We will describe its
+parameters:
+
+* first comes the form id (`entityForm`)
+
+* then two optional callbacks for the success and failure case
+
+* an array containing:
+
+  * a boolean which indicates status (success or failure), and then, on error:
+
+    * an array structured as ``[eid, {'rtype-role': 'error msg'}, ...]``
+
+  * on success:
+
+    * an url (string) representing the next thing to jump to
+
+Given the array structure described above, it is quite simple to
+manipulate the DOM to show the errors at appropriate places.
+
+Explanation
+~~~~~~~~~~~
+
+This mecanism may seem a bit overcomplicated but we have to deal with
+two realities:
+
+* in the (strict) XHTML world, there are no iframes (hence the dynamic
+  inclusion, tolerated by Firefox)
+
+* no (or not all) browser(s) support file input field handling through
+  ajax.
--- a/doc/book/en/devweb/edition/editcontroller.rst	Wed May 05 18:48:31 2010 +0200
+++ b/doc/book/en/devweb/edition/editcontroller.rst	Wed May 05 18:55:19 2010 +0200
@@ -1,72 +1,68 @@
 .. _edit_controller:
 
 The `edit controller`
-+++++++++++++++++++++
+---------------------
 
-It can be found in (:mod:`cubicweb.web.views.editcontroller`).
+It can be found in (:mod:`cubicweb.web.views.editcontroller`). This
+controller processes data received from an html form to create or
+update entities.
 
-Editing control
+Edition handling
 ~~~~~~~~~~~~~~~~
 
-.. XXX this look obsolete
+The parameters related to entities to edit are specified as follows
+(first seen in :ref:`attributes_section`)::
 
-The parameters related to entities to edit are specified as follows ::
-
-  <field name>:<entity eid>
+  <rtype-role>:<entity eid>
 
 where entity eid could be a letter in case of an entity to create. We
 name those parameters as *qualified*.
 
-1. Retrieval of entities to edit by looking for the forms parameters
-   starting by `eid:` and also having a parameter `__type` associated
-   (also *qualified* by eid)
+* Retrieval of entities to edit is done by using the forms parameters
+  `eid` and `__type`
 
-2. For all the attributes and the relations of an entity to edit:
+* For all the attributes and the relations of an entity to edit
+  (attributes and relations are handled a bit differently but these
+  details are not much relevant here) :
+
+   * using the ``rtype``, ``role`` and ``__type`` information, fetch
+     an appropriate field instance
 
-   1. search for a parameter `edits-<relation name>` or `edito-<relation name>`
-      qualified in the case of a relation where the entity is object
-   2. if found, the value returned is considered as the initial value
-      for this relaiton and we then look for the new value(s)  in the parameter
-      <relation name> (qualified)
-   3. if the value returned is different from the initial value, an database update
-      request is done
+   * check if the field has been modified (if not, proceed to the next
+     relation)
+
+   * build an rql expression to update the entity
 
-3. For each entity to edit:
+At the end, all rql expressions are executed.
 
-   1. if a qualified parameter `__linkto` is specified, its value has to be
-      a string (or a list of string) such as: ::
+* For each entity to edit:
+
+   * if a qualified parameter `__linkto` is specified, its value has
+     to be a string (or a list of strings) such as: ::
 
         <relation type>:<eids>:<target>
 
-      where <target> is either `subject` or `object` and each eid could be
-      separated from the others by a `_`. Target specifies if the *edited entity*
-      is subject or object of the relation and each relation specified will
-      be inserted.
+     where <target> is either `subject` or `object` and each eid could
+     be separated from the others by a `_`. Target specifies if the
+     *edited entity* is subject or object of the relation and each
+     relation specified will be inserted.
 
-    2. if a qualified parameter `__clone_eid` is specified for an entity, the
-       relations of the specified entity passed as value of this parameter are
-       copied on the edited entity.
-
-    3. if a qualified parameter `__delete` is specified, its value must be
-       a string or a list of string such as follows: ::
-
-          <ssubjects eids>:<relation type>:<objects eids>
+    * if a qualified parameter `__clone_eid` is specified for an entity, the
+      relations of the specified entity passed as value of this parameter are
+      copied on the edited entity.
 
-       where each eid subject or object can be seperated from the other
-       by `_`. Each relation specified will be deleted.
+    * if a qualified parameter `__delete` is specified, its value must be
+      a string or a list of string such as follows: ::
 
-    4. if a qualified parameter `__insert` is specified, its value should
-       follow the same pattern as `__delete`, but each relation specified is
-       inserted.
+          <subjects eids>:<relation type>:<objects eids>
 
-4. If the parameters `__insert` and/or `__delete` are found not qualified,
-   they are interpreted as explained above (independantly from the number
-   of entities edited).
+      where each eid subject or object can be seperated from the other
+      by `_`. Each specified relation will be deleted.
+
 
-5. If no entity is edited but the form contains the parameters `__linkto`
-   and `eid`, this one is interpreted by using the value specified for `eid`
-   to designate the entity on which to add the relations.
-
+* If no entity is edited but the form contains the parameters `__linkto`
+  and `eid`, this one is interpreted by using the value specified for `eid`
+  to designate the entity on which to add the relations.
 
 .. note::
 
--- a/doc/book/en/devweb/edition/examples.rst	Wed May 05 18:48:31 2010 +0200
+++ b/doc/book/en/devweb/edition/examples.rst	Wed May 05 18:55:19 2010 +0200
@@ -1,8 +1,50 @@
 Examples
 --------
 
-Example of ad-hoc fields form
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+(Automatic) Entity form
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Looking at some cubes available on the `cubicweb forge`_ we find some
+with form manipulation. The following example comes from the the
+`conference`_ cube. It extends the change state form for the case
+where a ``Talk`` entity is getting into ``submitted`` state. The goal
+is to select reviewers for the submitted talk.
+
+.. _`cubicweb forge`: http://www.cubicweb.org/view?rql=Any+P+ORDERBY+N+WHERE+P+name+LIKE+%22cubicweb-%25%22%2C+P+is+Project%2C+P+name+N
+.. _`conference`: http://www.cubicweb.org/project/cubicweb-conference
+
+.. sourcecode:: python
+
+ from cubicweb.web import formfields as ff, formwidgets as fwdgs
+ class SendToReviewerStatusChangeView(ChangeStateFormView):
+     __select__ = (ChangeStateFormView.__select__ &
+                   implements('Talk') &
+                   rql_condition('X in_state S, S name "submitted"'))
+
+     def get_form(self, entity, transition, **kwargs):
+         form = super(SendToReviewerStatusChangeView, self).get_form(entity, transition, **kwargs)
+         relation = ff.RelationField(name='reviews', role='object',
+                                     eidparam=True,
+                                     label=_('select reviewers'),
+                                     widget=fwdgs.Select(multiple=True))
+         form.append_field(relation)
+         return form
+
+Simple extension of a form can be done from within the `FormView`
+wrapping the form. FormView instances have a handy ``get_form`` method
+that returns the form to be rendered. Here we add a ``RelationField``
+to the base state change form.
+
+One notable point is the ``eidparam`` argument: it tells both the
+field and the ``edit controller`` that the field is linked to a
+specific entity.
+
+It is hence entirely possible to add ad-hoc fields that will be
+processed by some specialized instance of the edit controller.
+
+
+Ad-hoc fields form
+~~~~~~~~~~~~~~~~~~
 
 We want to define a form doing something else than editing an entity. The idea is
 to propose a form to send an email to entities in a resultset which implements
--- a/doc/book/en/devweb/js.rst	Wed May 05 18:48:31 2010 +0200
+++ b/doc/book/en/devweb/js.rst	Wed May 05 18:55:19 2010 +0200
@@ -22,8 +22,8 @@
 
 .. XXX external_resources variable (which needs love)
 
-CubicWeb javascript API
-~~~~~~~~~~~~~~~~~~~~~~~
+Server-side Javascript API
+~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 Javascript resources are typically loaded on demand, from views. The
 request object (available as self._cw from most application objects,
@@ -39,8 +39,8 @@
   snippet inline in the html headers. This is quite useful for setting
   up early jQuery(document).ready(...) initialisations.
 
-CubicWeb javascript events
-~~~~~~~~~~~~~~~~~~~~~~~~~~
+Javascript events
+~~~~~~~~~~~~~~~~~
 
 * ``server-response``: this event is triggered on HTTP responses (both
   standard and ajax). The two following extra parameters are passed
@@ -53,8 +53,8 @@
     ajax request, otherwise the document itself for standard HTTP
     requests.
 
-Important AJAX APIS
-~~~~~~~~~~~~~~~~~~~
+Important javascript AJAX APIS
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 * `asyncRemoteExec` and `remoteExec` are the base building blocks for
   doing arbitrary async (resp. sync) communications with the server
@@ -72,10 +72,10 @@
 A simple example with asyncRemoteExec
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-In the python side, we have to extend the BaseController class. The
-@jsonize decorator ensures that the `return value` of the method is
-encoded as JSON data. By construction, the JSonController inputs
-everything in JSON format.
+In the python side, we have to extend the ``BaseController``
+class. The ``@jsonize`` decorator ensures that the return value of the
+method is encoded as JSON data. By construction, the JSonController
+inputs everything in JSON format.
 
 .. sourcecode: python
 
@@ -225,13 +225,13 @@
 `http://myinstance/json?`). The actual JSonController method name is
 encoded in the `params` dictionary using the `fname` key.
 
-A more real-life example from CubicWeb
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+A more real-life example
+~~~~~~~~~~~~~~~~~~~~~~~~
 
-A frequent use case of Web 2 applications is the delayed (or
-on-demand) loading of pieces of the DOM. This is typically achieved
-using some preparation of the initial DOM nodes, jQuery event handling
-and proper use of loadxhtml.
+A frequent need of Web 2 applications is the delayed (or demand
+driven) loading of pieces of the DOM. This is typically achieved using
+some preparation of the initial DOM nodes, jQuery event handling and
+proper use of loadxhtml.
 
 We present here a skeletal version of the mecanism used in CubicWeb
 and available in web/views/tabs.py, in the `LazyViewMixin` class.
@@ -317,9 +317,6 @@
     }
 
 
-
-
-.. XXX reloadComponent
 .. XXX userCallback / user_callback
 
 Javascript library: overview
--- a/doc/book/en/devweb/rtags.rst	Wed May 05 18:48:31 2010 +0200
+++ b/doc/book/en/devweb/rtags.rst	Wed May 05 18:55:19 2010 +0200
@@ -9,8 +9,8 @@
 
 .. _uicfg:
 
-The ``uicfg`` module
-~~~~~~~~~~~~~~~~~~~~
+The uicfg module
+~~~~~~~~~~~~~~~~
 
 .. note::
 
--- a/migration.py	Wed May 05 18:48:31 2010 +0200
+++ b/migration.py	Wed May 05 18:55:19 2010 +0200
@@ -391,7 +391,7 @@
                 if optdict.get('default') is REQUIRED:
                     self.config.input_option(optdescr[1], optdict)
         self.config.generate_config(open(newconfig, 'w'))
-        show_diffs(configfile, newconfig)
+        show_diffs(configfile, newconfig, ask_confirm=self.confirm is not yes)
         os.close(fd)
         if exists(newconfig):
             os.unlink(newconfig)
--- a/web/data/cubicweb.edition.js	Wed May 05 18:48:31 2010 +0200
+++ b/web/data/cubicweb.edition.js	Wed May 05 18:55:19 2010 +0200
@@ -402,6 +402,7 @@
     }
     _displayValidationerrors(formid, descr[0], descr[1]);
     updateMessage(_('please correct errors below'));
+    // ensure the browser does not scroll down
     document.location.hash = '#header';
     return false;
 }