Using the CMFFormController

   The CMFFormController package helps developers by simplifying the
   process of validating forms. It also makes it easier for site
   administrators to override some of the behavior of packages without
   modifying code, making it easier to upgrade packages without
   disturbing the modifications.

   How it works:

   * Developers associate a set of default variables for their Page
     Templates.
     These variables control the validation that takes place after the
     form is submitted and the actions that occur after
     validation. The variables are stored on the filesystem in the
     .metadata properties file.

   * Site administrators can override the default validations and
     actions using the ZMI. Once a set of validations or actions has
     been specified in the ZMI, the default validations and actions
     will be ignored.

   Forms

      To take advantage of CMFFormController, you need to use
      Controller Page Templates rather than ordinary Page
      Templates. Controller Page Templates act just like ordinary Page
      Templates, but they do some extra work when they are viewed.

      Here is a basic form that uses CMFFormController::

        <form tal:define="errors options/state/getErrors"
              tal:attributes="action string:${here/absolute_url}/${template/id};"
             method="post">
           <input type="hidden" name="form.submitted" value="1" />
           <p tal:define="err errors/foo|nothing" tal:condition="err" tal:content="err" />
           <input type="text"
                  name="foo"
                  tal:define="val request/foo|nothing"
                  tal:attributes="value val" />
           <input type="submit" name="submit" value="submit" />
        </form>

      Let's take a look.

      * First, we note that the form is set up to submit to itself.
        *Forms must submit to themselves.*

      * Second, we see the special hidden variable 'form.submitted'.
        The controlled page template checks the REQUEST for
        form.submitted to see if the form has been submitted or if,
        instead, it has just been accessed, e.g. via a link.
        *Forms must contain the hidden variable 'form.submitted'*

      * At the beginning of the form we set the variable errors.  The
        errors dictionary comes from the state object which is passed
        in the template options.  The state object lets validators and
        scripts pass information to each other and to forms.  For our
        purposes, the most important information is the errors
        dictionary, which has entries of the form '{field_name:error_message}'.

      Before we can use this form we need to specify the validators
      that will be used to check the form values, and we need to
      specify the action that will occur after validation.

   Specifying Validators

         There are two basic ways to specify a form's validators.

            1. You can specify the validators in the .metadata
               properties for filesystem-based Controller Page
               Templates.

            2. You can specify the validators via the ZMI (or
               programmatically). These values will be stored in the
               ZODB as attributes of the 'portal_form_controller' object.

         If you specify validators in both places, the validators
         specified in the ZMI will take precedence over those
         specified in the form.

      Specifying Validators on the Filesystem

         You can specify validators on the filesystem using an objects
         .metadata properties file.

         To create a .metadata file, simply create a file with the
         same name as your page template, and then append .metadata to
         the end of the name of the file. For instance, you might have
         a Controller Page Template called
         'document_edit_form.cpt'. The properties for that file would
         be stored in a file called 'document_edit_form.cpt.metadata'

         The .metadata file uses the standard python ConfigParser
         syntax. The validator section of the .metadata file would
         look like::

           [validators]
           validators = validate_script1, validate_script2

         The validation scripts 'validate_script1' and
         'validate_script2' will be called in order.

         Type-Specific Validators

            Suppose you want different validators to be called,
            depending on the type of context the form has.

         You can do so as follows::

            [validators]
            validators = validate_script1
            validators.Document = validate_script2

         In the above example, if the context is a Document object,
         'validate_script2' will be called for validation; for
         everything else, only 'validate_script1' will be called.

         Note that the order in which the variables are specified does
         not matter; the type-specific validators override
         non-specific validators if both are applicable.

         Button-Specific Validators

            Suppose instead that you have two different buttons on
            your form, and you want different validation sequences to
            occur depending on which button is pressed.  You can
            accomplish this as follows:

         First, name your buttons button1 and button2::

               <input type="submit"
                      name="form.button.button1"
                      value="First Button" />
               <input type="submit"
                      name="form.button.button2"
                      value="Second Button" />

         Next, specify validators in the .metadata file for button1
         and for button2::

               [validators]
               validators..button1 = validate_script1, validate_script3
               validators..button2 = validate_script2, validate_script4

         Note the presence of the '..'.  This is a placeholder for a
         type specifier.  You could further specify that
         'validate_script5' is called if 'button2' is pressed and the
         context is a Document by adding::

               [validators]
               validators.Document.button2 = validate_script5

         Remember that button specific validators take precedence over
         non-specific validators.

      Specifying Validators in the ZMI

            If you look at a Controller Page Template in the ZMI, you
            will see that it looks just like an ordinary Page Template
            with two extra tabs, Validation and Actions. Click on the
            Validation tab.

            The Validation tab shows all the validators for the page
            template in question. You can specify validators with the
            same kind of specialization options as above via a web
            form.

            The validator information for all forms is stored in the
            'portal_form_controller' tool in your portal. This means
            that you can specify validators for filesystem objects
            with no problems, since the information is persisted in
            the ZODB. Note that the validator information is bound to
            the form's Id, so all forms with the same Id use the same
            validators. This keeps things simple when you have
            multiple skins:

            *Forms with the same Id use the same validators, no matter
            what skin they are in.*

            When a form is submitted, it first checks to see if there
            are any applicable validators that have been specified via
            the ZMI.  If it finds one, it uses it. If it does not find
            a validator via the ZMI, it then checks the REQUEST object
            to see if validators have been specified in hidden
            variables.  As a result, validators specified in the ZMI
            take precedence over those specified in forms.

         Specifying Validators Programmatically

            The portal's 'portal_form_controller' tool has methods you
            can use to specify the validators for a given
            ControllerPageTemplate. The API is as follows::

              portal_form_controller.addFormValidators(id,
                                                       context_type,
                                                       button,
                                                       validators)

            Here 'id' is the Id of the ControllerPageTemplate,
            'context_type' is the class name for the class of the
            context object, 'button' is the name of the button
            pressed, and validators is a comma-delimited string or a
            list of strings. If you want a validator to act for any
            class, set context_type to None.
            Similarly, you want a validator to act for any button, set
            button to None.

   Specifying Actions

         The sequence of validators that is executed returns a status
         in the state object.
         The default status is 'success', i.e. if no validators are
         executed, the status will be 'success'. If a validator
         encounters an error, it will typically set the status to
         'failure'.  The next thing we need to do in your form is to
         specify what happens when a given status is returned.

         As with validators, there are two basic ways to specify a
         form's actions.

            1. You can specify the actions in the .metadata properties
               for filesystem-based Controller Page Templates and
               Controller Python Scripts.

            2. You can specify the actions via the ZMI (or
               programmatically). These values will be stored in the
               ZODB as attributes of the 'portal_form_controller' object.

         If you specify actions in both places, the actions specified
         in the ZMI will take precedence over those specified in the
         form.

         Specifying Actions on the Filesystem

            You can specify actions on the filesystem using an objects
            .metadata properties file.

         Actions are stored in the same .metadata file as the
         validators. The syntax for the actions section of your file
         would look like::

           [actions]
           action.success = traverse_to:string:script1

         In the above example, when the form is submitted and the
         validation scripts return a status of 'success', the
         'traverse_to' action is called with the argument
         'string:script1', i.e. if the form data is valid, we run the
         script 'script1'.  Alternatively, we could specify
         'action.success = redirect_to:string:http://my_url_here',
         which would cause the browser to be redirected to
         'http://my_url_here'.

          The default action for the 'failure' status is to reload the
          current form. The form will have access to all the error
          messages, via the state object in its options.

            Type-Specific Actions

               Suppose you want different actions to occur depending
               on the type of context the form has.

            You can do so as follows::

                  [actions]
                  action.success = traverse_to:string:script1
                  action.success.Document = traverse_to:string:document_script

            In the above example, if the context is a Document object,
            document_script will be executed upon successful
            validation; for everything else, script1 will be executed.
            Note that the order in which the variables are specified
            does not matter; the type-specific actions will override
            non-specific actions if both are applicable.

            Button-Specific Actions

               Suppose instead that you have two different buttons on
               your form, and you want different actions to occur
               depending on which button is pressed.  You can
               accomplish this as follows:

            First, name your buttons button1 and button2::

               <input type="submit"
                      name="form.button.button1"
                      value="First Button" />
               <input type="submit"
                      name="form.button.button2"
                      value="Second Button" />

            Next, specify actionss for button1 and for button2::

               [actions]
               action.success..button1 = traverse_to:string:script1
               action.success..button2 = traverse_to:string:script2

            Note the presence of the '..'.  This is a placeholder for
            a type specifier.  You could further specify that
            'document_script2' is called if button2 is pressed and the
            context is a Document by adding::

               [actions]
               action.success.Documnet.button2 = traverse_to:string:document_script2

         Specifying Actions in the ZMI

            If you look at a Controller Page Template in the ZMI, you
            will see that it looks just like an ordinary Page Template
            with two extra tabs, Validation and Actions. Click on the
            Actions tab.

            The Actions tab shows all the actions for the page
            template in question. You can specify actions with the
            same kind of specialization options as above via a web
            form.

            The action information for all forms is stored in the
            'portal_form_controller' tool in your portal.  This means
            that you can specify actions for filesystem objects with
            no problems, since the information is persisted in the
            ZODB. Note that the action information is bound to the
            form's Id, so all forms with the same Id use the same
            actions. This keeps things simple when you have multiple
            skins: forms with the same Id use the same actions, no
            matter what skin they are in.

            When a form is submitted, it first checks to see if there
            are any applicable actions that have been specified via
            the ZMI. If it finds one, it uses it. If it does not find
            an action via the ZMI, it then checks the REQUEST object
            to see if actions have been specified in hidden
            variables. As a result, actions specified in the ZMI take
            precedence over those specified in forms.

         Specifying Actions Programmatically

            The portal's 'portal_form_controller' tool has methods you
            can use to specify the actions for a given
            ControllerPageTemplate. The API is as follows::

               portal_form_controller.addFormAction(id,
                                                    status,
                                                    context_type,
                                                    button,
                                                    action_type,
                                                    args)

            Here 'id' is the Id of the ControllerPageTemplate,
            'status' is the status for which the action will be
            executed, 'context_type' is the class name for the class
            of the context object, 'button' is the name of the button
            pressed, 'action_type' is the type of action that will
            occur, and 'args' is a string (typically a TALES
            expression) that will be passed to the action. If you want
            an action to be executed for any class, set context_type
            to None. Similarly, you want an action to be executed for
            any button, set button to None.

   Validation Scripts

      When writing validation scripts, use Controller Validators
      instead of Python Scripts. Controller Validators are just like
      ordinary Scripts with the addition of a ZMI Actions tab.  On the
      file system, Controller Validators use the extension .vpy rather
      than .py.

      Let's take a look at a basic validation script that tests the
      REQUEST value 'n' to see if it is an integer::

         n = context.REQUEST.get('n', None)
         if not n:
            state.setError('n', 'Please enter a value', new_status='failure')
         else:
            try:
               int(n)
            except ValueError:
               state.setError('n', 'Please enter an integer',
                              new_status='failure')

         if state.getErrors():
            state.set(portal_status_message='Please correct the errors shown.')
         return state

      The first thing to note is that Controller Validators have a
      built-in state object called 'state'.  This state object (of
      class ControllerState) contains basic information about what has
      happened during the validation chain.

      The state object has a 'status' attribute which contains the
      current validation status. The initial status is 'success'. If
      errors are detected by validators, they set the status to
      something else, typically 'failure'.

      The state object also stores errors that have been detected. The
      'setError' method is used to set an error message for a
      particular variable. The setError method has the optional
      'new_status' argument that can be used to both set an error
      message as well as to update the status. You can see if an error
      message has already been stored for a particular variable by
      calling 'state.getError(variable_name)'.

      The set method lets you set multiple attributes of the state
      object all at once, e.g.::

         state.set(status='my_new_status')

      You can also pass keyword arguments to the state object via the
      set method. These arguments will get passed along by the
      action. The 'traverse_to' action places these keyword arguments
      in the REQUEST. The 'redirect_to' action adds them to the query
      string of the URL to which it is redirecting.

      Finally, we return the state object.

   Scripts

      When writing scripts that do some processing after a validated
      form, you can use Controller Python Scripts instead of ordinary
      Python Scripts to let site managers override their actions via
      the ZMI.  On the file system, Controller Python Scripts use the
      extension .cpy rather than .py.  Note that Controller Validators
      and Controller Python Scripts differ in signficant ways.  Be
      sure to use the appropriate script type (Controller Validator or
      Controller Python Script) and/or the appropriate file extension
      (.cpy or .vpy).

   Let's take a look at a basic script that sets a context attribute
   to the value 'n' that is passed in via the 'REQUEST'::

      context.n = context.REQUEST.get('n')

      # Optionally set the default next action (this can be overridden
      # in the ZMI)

      state.setNextAction('redirect_to:string:view')

      # Optionally pass a message to display to the user
      state.setKwargs({'portal_status_message':'You set context.n to %s.' % str(context.n)})
      return state

   Note that you will usually want to use the 'traverse_to' action to
   call your script. This will ensure that form variables set in the
   REQUEST object are available to your script.

   This script sets its action to redirect to the relative url 'view'
   for the current context object. The status has not been set, so it
   is the default status, 'success'.

   The 'state.setNextAction' directive above is analogous to having
   the following line in your .metadata file::

        [actions]
        action.success = redirect_to:string:view

   As with the .metadata file, the default action specified in the
   script can be overridden via the ZMI.  This allows site managers to
   override post-script actions without having to customize your code.

   Finally, we return the state object.

   Validation for Scripts

      Having separate validation scripts typically means that
      validation is moved out of scripts.  This simplifies scripts,
      but means that it is possible to call them directly with invalid
      data.  We can prevent this problem by adding validators to
      scripts.  Controller Python Scripts use the same ZMI and/or
      .metadata file mechanisms for adding validators as do Controller
      Page Templates.

      Each time a validator is called, it logs the call in the state
      object. Validation is smart enough that if a validator is called
      by a form, it will not be called again by the script.

      Note that if you associate validators with a script, you will
      need to set a sensible 'failure' status action, since scripts do
      not set such an action by default.  You may wish to define a
      different failure status for failures that occur within your
      script, e.g. 'script_failure'.  Then you can specify a behavior
      for failures that occur as a result of invalid parameters coming
      in and for failures that occur within the script.
