You are not logged in Log in Join
You are here: Home » Members » jack-e » FormsTool for CMF » CMFFormsTool Wiki » LetterToAFriend

Log in
Name

Password

 
 

History for LetterToAFriend

??changed:
-
This is a letter to a friend that I wrote, trying to describe the CMFFormsTool.

    '----snip----'

    I cannot yet promise that portal_forms is able to handle your project nor
    that it is bug-free .. basically following things are possible:


      - multiple forms-collections can be mapped onto each ContentType ..
	see it as a mixture of portal_skins and portal_workflow

      - a forms-collection holds Formulator-Forms, PageTemplates and PythonScripts
	there is another mapping where one can select a template (which has several views)
	for a form to display and the Scripts as actions


    due to the two mappings it is highly configurable and every forms-collection
    offers all forms with certain views and actions.


    how it works:


      you have a PageTemplate in one of your portal_skin-folders e.g. "custom"
      that has the name of your action. e.g. "edit_rack",the template might look like::

       <html metal:use-macro="container/main_template/macros/master">
       <body>
	<div metal:fill-slot="main" tal:define="viewid request/view | string:standard">
	  <div metal:use-macro="python:here.portal_forms.render_form(here,'masterrack_edit',viewid,REQUEST=request).macros['form']" />
	</div>
	</body>
       </html>


      you see that the portalforms tool has a method render_form that takes the
      content-object (here), a formid (name
      of the formulator-form), a viewid (default is "standard" allthough one can
      change this too) and the request.
      the method gives you back a freshly wrapped PageTemplate whose macro you can
      use then.

      and another template in your formscollection e.g. "dtemplate" that is mapped
      to your form::

       <html>
	<body>
	 <div metal:define-macro="standard">
	  <span tal:replace="structure form/header" />
	  <table metal:define-slot="Default" />
	  <table metal:define-slot="Buttons" />
	  <span tal:replace="structure form/footer" />
	 </div>
	</body>
       </html>

      this is the part of your page that is the forms ..
      every macro you define here, is a view that can be used to display this form.
      this could be used for beginner/advanced, different languages etc.
      there are two slots defined by default: "Default" this is the place
      where the form-elements are rendered that are in the "Default"-group
      of Formulator. "Buttons" is a simple table that has buttons for all
      actions that are defined for the form (the scripts btw. the script-title
      is used for the buttons value..)

      I use the feature from Formulator to group fields to enable
      partls of forms that can be inserted in the page at the right place.

      for every group-of-formfields you can define a slot in the template ..
      or not :) . then you only see parts of the form-elements (beginner-view)

      the form-elements in a group are rendered as a simple table with
      2 rows (label, widget), the help-text is displayed below the widget
      as well as the error-text that can be supplied. when an error occured
      there is no exception raised but the form with the original-values from
      the request is shown again, and the fields that were wrong are marked
      with a different CSS-Class (label, label_with_error)

      this is the only part that gets rendered by the engine and is not
      customizeable TTW.
      the class for this is separated though and only 50 lines of code ..


      the following steps happen when a form is rendered:

      - first the form is retrieved for the portaltype otherwise an error is raised

      - the template for the form is retrieved

      - a new pagetemplate is initialized with a generated source that offers the macro "form"
	and uses the macro <viewid>. it just fills the slots of "dtemplate" with the form-group tables.
	
      - the template is returned, the macro is included into your page and then
        executed by the TAL-engine ( there is another tweaking-possibility that i found out today:
	you can specify an "extra"-property in each formfield that is raw-text and is applied to
	the input-tag while it's rendered. this enables you to write tal-statements into each formfield.
	you can easily retrieve values that may be a result from a call)
	basically the formstool tries to get the "alternate_name" or "id" of each
        formelement as attribute from the context and then calls it if it is a function otherwise returns
        it or the default-value is used from formulator (incl overrides ..). there are at least 2
        possiblities before the form is rendered and 1 after to modify the values, styles, etc.

      when you included the "Buttons"-slot into your macro you'll get somthing
      like::

       <table border="0">
	<tr>
	 <td valign="top"><input type="submit" name="just_display:method" value="just display" /></td>
	 <td valign="top"><input type="submit" name="save_data:method" value="save"/></td>
	</tr>
       </table>

      the method-names are then called on the result object (you need to define an
      url in the formulator forms-definition, that handles your request. a single script is
      enough, I usually call it "formreply", it is stored in the portal_skins-folder and
      looks like::

       ## Script (Python) "formreply"
       ##bind container=container
       ##bind context=context
       ##bind namespace=
       ##bind script=script
       ##bind subpath=traverse_subpath
       ##parameters=portal_forms_name,portal_forms_view
       ##title=handle a formreply
       ##
       request = context.REQUEST
       pf = context.portal_forms


       valid,result =
       pf.validate_request(context,portal_forms_name,portal_forms_view,REQUEST=requ
       est)

       if not valid:
	   request.set('name',portal_forms_name)
	   request.set('view',portal_forms_view)
	   request.set('validation_errors',result)
	   return context.form()
       else:
	   handler_func = traverse_subpath[0]
	   return pf.exec_script(context,portal_forms_name,handler_func,result)

      this script returns to the original form (portal_forms_name and portal_forms_view are reserved words)
      if an validation-error occured and calls the function that is given by the traverse_subpath (your submit-method)
      with the validated result. If you define fields in a form outside the scope of formulator you need to change things
      here probably too.


      You can now have an simple script that is close to the form and its template. there are no blind-passengers
      for result-functions in the skins-folder anymore .. the skins-folder has only actions that the user perfoms or
      content-relevant skins. every action that needs a form is redirected to the portal_forms tool and the validation
      and result are handled just by the routing in "formreply" and your action-script, that fires the workflow-actions
      depending on success or not..

    '----snip----'