=============
Writing Forms
=============

.. warning:: This documentation is very outdated. Keeping for historical reference.

------------
Introduction
------------

Since, in the Plone Intranet project, we are following a design driven model,
we cannot rely on a form generating library (such as z3c.form) to generate our
forms for us, as the markup generated by this library does not conform to the
markup in the design prototype.

Instead of using z3c.form to render our forms, we hand code our forms based
on the markup found in the prototype.

We do however want to stay as close and true as possible to existing
development patterns used in Plone. Therefore, we create custom Dexterity
add/edit views for all Dexterity content types, and put the prototype markup in
their templates.

Even though forms are hand-coded (by the designer), form submission, handling
and content type creation/modification are all still handled by Dexterity/z3c.form.

However, in order to have z3c.form handle our submitted forms, we need to add
some extra markup (such as special ``name`` attributes to our ``input``
elements) to our custom form markup.

-------------------------------------------------
Custom Add/Edit views for Dexterity content types
-------------------------------------------------

Each Add or Edit view consists of two parts. The form and a view which wraps it
in a specific layout (also called a form wrapper).

Custom view (form wrapper) layout
=================================

Generally you wouldn't care about the form wrapper view's layout, but in the case of
Plone Intranet, we need fully custom designer specified markup, so it was  necessary
to modify this layout.

Luckily, this only needed to be done once, and then all custom add/edit views
will reuse it.

For posterity, here's how it's done.

You need to register a ``layout_factory`` adapter. See::

    ploneintranet/theme/browser/templates.py
    
This adapter has a layout template::

    ploneintranet/theme/browser/templates/layout.pt
    
and a corresponding ZCML registration snippet::

    ploneintranet/theme/browser/configure.zcml

The form
========

The forms are subclasses of the default Dexterity add and edit forms, together
with a mixin class ``DexterityFormMixin`` (the point of this mixin class is
explained in the `templates`_ section::


    class AddForm(add.DefaultAddForm, views.DexterityFormMixin):
        """ Custom add form for the Document content type.
        """
        template = ViewPageTemplateFile('templates/edit_document.pt')


.. warning:: This template does not exist anymore.
        
Additionally, for each form we need to provide a custom template which contains
the markup from the prototype.

In the example above, we specify the custom template as an attribute on the
class.

Custom marshalling of data
--------------------------

In computer science the act of "marshalling" refers to transforming a certain
representation of data into a different format suitable for transmission or
computation.

z3c.form expects the form request data to conform to certain expectations, such
as that the keys of the request values need to have certain specific values,
like ``form.widgets.IDublinCore.title`` for the title field.

We can fix this either by modifying the template markup (e.g. by adding a
custom ``name`` attribute on the ``title`` widget with value ``form.widgets.IDublinCore.title``)
or we can handle it server side in the ``extractData`` method::

    def extractData(self, setErrors=True):
        exclude_from_nav = ''
        if not self.request.get(exclude_from_nav):
            # XXX: This is a required field, but not in the form. Not yet sure
            # what the right approach to deal with it is.
            # Either we deal with it here, or add a hidden field in the
            # template.
            self.request.form[exclude_from_nav] = 'selected'
        return super(AddForm, self).extractData()


The view (aka form wrapper)
===========================

For each form (i.e. add and edit), we need a view which acts as a form
wrapper::

    class EditView(edit.DefaultEditView):
        """ Custom edit view for the Document content type.
        """
        form = EditForm


The view subclasses the default add and edit views from ``plone.dexterity``.
Each view also requires a ``form`` attribute to be set which points to the form
created in the `section above <The form>`_.

Register add/edit views with zcml
---------------------------------

The views need to be registered in zcml (in ``ploneintranet.theme/browser/configure.zcml``).

The add view is a special case, since it's an adapter on the ``++add++``
namespace::

  <adapter
      for="Products.CMFCore.interfaces.IFolderish
           ..interfaces.IThemeSpecific
           plone.dexterity.interfaces.IDexterityFTI"
      factory=".document.AddView"
      name="Document"
      provides="zope.publisher.interfaces.browser.IBrowserPage"
      />

Important parts to note is the ``factory`` directive, which points to the view
class as well as the second interface passed to the ``for`` directive, which is
the theme layer for ``ploneintranet.theme``.


The edit view is registered like a normal browser page::

  <browser:page
      for="plone.app.contenttypes.interfaces.IDocument"
      name="edit"
      class=".document.EditView"
      permission="cmf.ModifyPortalContent"
      />

Templates
=========

Widget markup
-------------

The markup in the templates should come straight from the prototype (i.e. design driven).

However, in order to have z3c.form handler form submission, we need z3c.form to
render the form buttons and also add special ``name`` attributes to our form
inputs.

Additionally, for dynamic rendering, we need to sprinkle the markup with ``TAL`` statements,
which won't be found in the prototype.

For example::

    <fieldset class="horizontal">
        <label>Title <sup class="required">*</sup> <a href="tooltip-help.html#subject-2" class="iconified icon-info-circle help pat-tooltip" data-pat-tooltip="trigger: click; source: ajax">More info</a>
            <input type="text"
                   name="form.widgets.IDublinCore.title"
                   tal:attributes="value view/title|nothing"/>
        </label>
    </fieldset>

Note the ``name`` attribute which has a value of ``form.widgets.IDublinCore.title``.
The input needs this ``name`` attribute for z3c.form to properly parse the
request.

To get the correct ``name`` attribute, I inspect a vanilla Plone form of the same content type.

Note also the ``tal:attributes`` statement which ensures that the widget will
have the correct default value set.

The fact that ``view/title`` (or ``python:view['title']``) can be called on the form,
is due to the ``DexterityFormMixin`` class mentioned above. This class provides a custom
``__getitem__`` method which will look up the requested attribute, first in the
request, and then on the context (in the case of edit forms).

Unused but required fields
--------------------------

If your content type has required fields, which do not have widgets in the
prototype, you'll need to still add them to the form as hidden inputs,
otherwise z3c.form will complain that they aren't set::

    <!-- XXX: This is a required field, but will not be used in Plone Intranet -->
    <input type="hidden" value="selected" name="form.widgets.IExcludeFromNavigation.exclude_from_nav"/>

Form buttons
------------

z3c.form handles form submission via so-called button handlers. In order for
this to work, the buttons need special markup.

One way to do this, would be to manually edit the markup from the prototype, so
that it has the necessary attributes that z3c.form expects (likely only
``name``).

However, I've overridden the default button markup from z3c.form (see
``ploneintranet/theme/browser/templates/submit_input.pt``) to match the proto's
markup.

Here's an example how to render buttons via z3c.form::

    <fieldset class="button-bar group">
        <tal:block repeat="action view/actions/values">
            <button type="submit" tal:replace="structure action/render" />
        </tal:block>
    </fieldset>
