diff -r 37a2455639b9 -r c6c9a80ad1dd doc/book/en/devweb/edition/dissection.rst --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/book/en/devweb/edition/dissection.rst Tue May 04 19:16:59 2010 +0200 @@ -0,0 +1,276 @@ + +Dissection of a Form +-------------------- + +This is done (again) with a vanilla instance of the `tracker`_ +cube. We will populate the database with a bunch of entities and see +what kind of job the automatic entity form does. + +.. _`tracker`: http://www.cubicweb.org/project/cubicweb-tracker + +Patching the session object +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to play interactively with web side application objects, we +have to cheat a bit: we will decorate the session object with some +missing artifacts that should belong to a web request object. With +that we can instantiate and render forms interactively. + +The function below does the minimum to allow going through this +exercice. Some attributes or methods may be missing for other +purposes. It is nevertheless not complicated to enhance it if need +arises. + +.. sourcecode:: python + + def monkey_patch_session(session): + """ useful to use the cw shell session object + with web appobjects, which expect more than a plain + data repository session + """ + # for autoform selection + session.json_request = False + session.url = lambda: u'http://perdu.com' + session.session = session + session.form = {} + session.list_form_param = lambda *args: [] + # for render + session.use_fckeditor = lambda: False + session._ressources = [] + session.add_js = session.add_css = lambda *args: session._ressources.append(args) + session.external_resource = lambda x:{} + session._tabcount = 0 + def next_tabindex(): + session._tabcount += 1 + return session._tabcount + session.next_tabindex = next_tabindex + return session + +Populating the database +~~~~~~~~~~~~~~~~~~~~~~~ + +We should start by setting up a bit of context: a project with two +unpublished versions, and a ticket linked to the project and the first +version. + +.. sourcecode:: python + + >>> p = rql('INSERT Project P: P name "cubicweb"') + >>> for num in ('0.1.0', '0.2.0'): + ... rql('INSERT Version V: V num "%s", V version_of P WHERE P eid %%(p)s' % num, {'p': p[0][0]}) + ... + + + >>> t = rql('INSERT Ticket T: T title "let us write more doc", T done_in V, ' + 'T concerns P WHERE V num "0.1.0"', P eid %(p)s', {'p': p[0][0]}) + >>> commit() + +Now let's see what the edition form builds for us. + +.. sourcecode:: python + + >>> monkey_patch_session(session) + >>> form = session.vreg['forms'].select('edition', session, rset=rql('Ticket T')) + >>> html = form.render() + +This creates an automatic entity form. The ``.render()`` call yields +an html (unicode) string. The html output is shown below (with +internal fieldset omitted). + +Looking at the html output +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The form enveloppe +'''''''''''''''''' + +.. sourcecode:: html + +
main informations
+
+
+
validating...
+
+ + + + + + + + ... +
+
+
+ +The main fieldset encloses a set of hidden fields containing various +metadata, that will be used by the `edit controller` to process it +back correctly. + +The `freezeFormButtons(...)` javascript callback defined on the +``conlick`` event of the form element prevents accidental multiple +clicks in a row. + +The ``action`` of the form is mapped to the `validateform` controller +(situated in :mod:`cubicweb.web.views.basecontrollers`). + +The attributes section +'''''''''''''''''''''' + +We can have a look at some of the inner nodes of the form. Some fields +are omitted as they are redundant for our purposes. + +.. sourcecode:: html + +
+ + + + + + ... (description field omitted) ... + + + + + ... (type field omitted) ... + + + + + + + + +
+ +
+ +
importance
+
+ +
+ +
version in which this ticket will be / has been done
+
+
+ + +Note that the whole form layout has been computed by the form +renderer. It is the renderer which produces the table +structure. Otherwise, the fields html structure is emitted by their +associated widget. + +While it is called the `attributes` section of the form, it actually +contains attributes and *mandatory relations*. For each field, we +observe: + +* a dedicated row with a specific class, such as ``title_subject_row`` + (responsability of the form renderer) + +* an html widget (input, select, ...) with: + + * an id built from the ``rtype-role:eid`` pattern + + * a name built from the same pattern + + * possible values or preselected options + +The relations section +''''''''''''''''''''' + +.. sourcecode:: html + +
+ This ticket : + + + + +
+ + + + + + +
  
+ +
+
+
+ +The optional relations are grouped into a drop-down combo +box. Selection of an item triggers a javascript function which will: + +* show already related entities in the div of id `relatedentities` + using a two-colown layout, with an action to allow deletion of + individual relations (there are none in this example) + +* provide a relation selector in the div of id `relationSelector_EID` + to allow the user to set up relations and trigger dynamic action on + the last div + +* fill the div of id `unrelatedDivs_EID` with a dynamically computed + selection widget allowing direct selection of an unrelated (but + relatable) entity or a switch towards the `search mode` of + |cubicweb| which allows full browsing and selection of an entity + using a dedicated action situated in the left column boxes. + + +The buttons zone +'''''''''''''''' + +Finally comes the buttons zone. + +.. sourcecode:: html + + + + + + + + +
+ + + + +
+ +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 ?