-------------------
*** PORTAL_FORM ***
-------------------
The portal_form tool provides validation and navigation services for forms.
The built-in objects in Plone are now wired up to use the portal_form tool.

To see how portal_form works, let's take a look at the process of editing a
Link:

The first thing to notice is that link editing occurs via a new type of URL.
Before one edited links by visiting .../myLink/link_edit_form.  The action
of this form was a script, ../myLink/link_edit, which called a validator and
invoked navigation depending on the outcome of the validation.  If
validation failed, you would be shown an error page; if it succeeded, you
would be sent to some final destination page.

The new URL for editing links is ../myLink/portal_form/link_edit_form.  The
action of the new form is itself, ../myLink/portal_form/link_edit_form.  The
portal_form tool intercepts the url's traversal and checks for newly
submitted values.  If it sees new values, portal_form finds a set of validators
for the form in the portal_properties/form_properties sheet and invokes them
on the submitted values.  The chain of validators returns either 'success' or
'failure'.  Portal_form looks in portal_properties/navigation_properties to 
see what should happen next, and it hands off to the appropriate destination.
Because portal_form handles the invocation of the validation and the
navigation, all your form processing code has to do is to update your object.
The end result: simple, modular validators and simple form processing code, all
of which can be reconfigured using the property sheets in portal_properties.

Here's what you need to do to make portal_form work for your forms:

(1) In your form: Change the action of your form so that it submits to
itself (set the action to request/URL -- see link_edit_form).
Add a hidden variable called form_submitted and set its value to template/id.
Portal_form tests REQUEST.form_submitted to determine whether a form has been
submitted or not.  The changes to link_edit_form are commented.

(2) Register a set of validators for your form.  Registration takes place
via a call to portal_form's setValidators() method.  The first argument is
the name of the form, and the second is a list of validators.  For example,

    portal_form.setValidators('link_edit_form', ['validate_id', 'validate_link_edit'])

You can also register validators manually through portal_properties/form_properties.

The validators in the list are called in order.  In the example above, validate_id
is a generic validator used by most forms to check an object's id, and
validate_link_edit checks properties specific to the Link class.

(3) Write a validator for your form.  Your validator should return (1) a status
(usually either 'success' or 'failure'), (2) a set of error messages in a dictionary,
and (3) a dictionary of properties that are passed along to the next navigation state.
If your validator is part of a chain, you may want to access the status / errors
returned by the previous validator in the chain.  These values are available in the
REQUEST as REQUEST['validation_status'] and REQUEST['errors'].

One common application of the properties dictionary is to pass along a portal status
message by setting the key 'portal_status_message'.  The navigation tool will pass
these properties along to the next navigation state, either in the REQUEST (when the
next state is a page template or a python script) or as query parameters (when the
next state is a URL or action).  Note that if you invoke a script after validation,
you will need to have the script get your portal_status_message out of the REQUEST
and pass it along to the next navigation state.

(4) Write a form handler.  The handler should modify your object and its metadata from the
REQUEST and then return a status code.  Any additional code for invoking the validator
and for performing navigation is now unneeded and should be deleted.  Your form handler
should return a tuple consisting of a status (usually either 'success' or 'failure'),
the context that should be used by the next navigation state (returning a context allows
objects to be instantiated through portal_factory -- see below), and a dictionary of
properties that will be passed along to the next navigation state.

(5) Set up your form's desired navigation in portal_navigation (see below)


-------------------------
*** PORTAL_NAVIGATION ***
-------------------------
The portal_navigation tool is part of a controller for handling navigation
in forms.  Navigation is completely configurable through the
navigation_properties sheet in portal_properties.  The format of a
navigation property is as follows:

[type].[action].[status] = [next thing to do]

For example, the navigation for editing a link looks like so:

link.link_edit_form.failure = link_edit_form
link.link_edit_form.success = script:link_edit
link.link_edit.success = action:view

The navigation tool implments the state controller pattern from Design
Patterns, and the navigation_properties sheet is a state transition table.
The left hand side of the navigation properties above indicates the current
state, and the right hand side indicates the transition.

For example,

link.link_edit_form.failure = link_edit_form

means that after receiving an outcome of 'failure' from validating
link_edit_form on a link, the navigation tool should re-invoke the
page template link_edit_form on the current context.

link.link_edit_form.success = script:link_edit

means after receiving an outcome of 'success' from validating
link_edit_form on a link, invoke the script link_edit on the current
context.

link.link_edit.success = action:view

means after receiving an outcome of 'success' from invoking link_edit
on a link, perform the view action on the current link.

The transitions specified on the right hand side can be the following:

PAGE_TEMPLATE -- invoke the page template PAGE_TEMPLATE on the current context
action:ACTION -- invoke the action ACTION on the current context
script:SCRIPT -- invoke the script SCRIPT on the current context
url:URL -- redirect to the specified (absolute) URL

In addition, the transitions can incorporate data from the request via
brackets.  For example, url:http://www.zope.org/?myId=[id] will send the
user to the specified URL with [id] replaced by the value of REQUEST.id.


----------------------
*** PORTAL_FACTORY ***
----------------------
The portal_factory tool lets you create new objects without resulting in
orphaned objects if the user bails out of the object creation form before
finishing.  Portal_factory is still experimental, but it should work.
Portal_factory will most likely be enabled in Plone 1.1.

Portal_factory works in conjunction with portal_form, and its services are
invoked via URL mangling.  Rather than specifying in a url the object on
which a form will operate, you insert "portal_factory/OBJECT_TYPE" into the
url where the object's ID would have gone.  So to create a link using the
edit_link_form, one sends the user to
.../portal_factory/Link/portal_form/link_edit_form.

When the link .../portal_factory/Link/portal_form/link_edit_form is invoked,
portal_factory intercepts traversal and sees that the user will be creating
a Link object upon successful submission of the link_edit_form.  It
generates an object ID for the new Link, say Link.2002-08-19.114019,
relocates to .../portal_factory/Link.2002-08-19.114019/portal_form/link_edit_form,
and inserts a placeholder object in the traversal stack.  Link_edit_form will
see this placeholder object as its context.  The placeholder has only two
public attributes, an id and a title, it knows what type of object is
supposed to be created when the form is submitted (accessible via getTypeInfo()),
and it has an invokeFactory method for creating an object of the appropriate type
once form submission is successful.

Once you are using portal_form, you don't need to do much to get this to work
with your code.  Your edit page has to be robust to dealing with an object
that has no attributes other than id / title, and you need to call the object's
invokeFactory method in your object_edit script and return the created
object as the new context.  Portal_factory has a convenience method called
createObject which does nothing when portal_factory is not being used, so
it's safe to call.  See link_edit.py in skins/plone_scripts/form_scripts
for an example.

One cool thing is that portal_factory behaves nicely when you create an
object and then navigate back using the BACK button on your browser.  Things
work as expected because portal_factory checks to see if the temporary
object specified in the url already exists in the ZODB: if it does,
portal_factory hands the real object to the form as its context.

Portal_factory currently creates an empty placeholder object that is not
persisted between requests.  That approach is fine for simple objects, but
not so great when object creation involves multiple forms and your objects
are complicated (e.g. contain subobjects, etc).  It is probably better to use
a real instance of the desired class that is stored in a temp_folder as the
placeholder.  That way one can build up an object over the course of
several forms.  Some form of garbage collection would be used to get rid of
objects for which the user does not complete construction.  The current API's
should allow this change to occur transparently.

