Forms#

Todo

Describe how create forms with Plone's default form framework z3c.form. Fields, Widgets, Vocabularies are also described in detail in their own chapters, and will be referenced from examples here.

Plone uses the z3c.form library to build its web forms. The packages responsible for integrating z3c.form with Plone are plone.z3cform and plone.app.z3cform, which contain most of the widgets and default templates. To simplify the process of organizing a form and specifying its widgets and fields, Plone utilizes plone.autoform, in particular its AutoExtensibleForm base class. It is responsible for handling form hints and configuring z3c.form widgets and groups (fieldsets).

A form is a view that uses these libraries to generate forms.

General forms#

The plonecli provides you with an option to add a form to your Plone package. Let's assume you created an add-on package with plonecli called collective.awesomeaddon.

cd collective.awesomeaddon
plonecli add form

After using the plonecli to add a form, you'll have a new sub folder forms in your package. Here you will find a configure.zcml containing the registration of the form.

<!-- ZCML header and other ZCML here  -->
<browser:page
  name="my-form"
  for="*"
  class=".my_form.MyForm"
  permission="cmf.ManagePortal"
  layer="p6.theme5.interfaces.IP6Theme5Layer"
  />
  <!-- further ZCML and ZCML footer go inside the `browser:page` node  -->

And a Python file with the code.

from plone import schema
from plone.autoform.form import AutoExtensibleForm
from z3c.form import button, form
from zope.interface import Interface


class IMyForm(Interface):
    """ Schema Interface for IMyForm
        Define your form fields here.
    """
    name = schema.TextLine(
        title="Your name",
    )


class MyForm(AutoExtensibleForm, form.EditForm):
    schema = IMyForm
    ignoreContext = True

    label = "What's your name?"
    description = "Simple, sample form"

    @button.buttonAndHandler("Ok")
    def handleApply(self, action):
        data, errors = self.extractData()
        if errors:
            self.status = self.formErrorsMessage
            return

        # Do something with valid data here

        changes = self.applyChanges(data)
        # Set status on this form page
        # (this status message is not bind to the session and does not go thru redirects)
        if changes:
            self.status = "Settings saved"

    @button.buttonAndHandler("Cancel")
    def handleCancel(self, action):
        """User cancelled. Redirect back to the front page.
        """

Our form MyForm is a subclass of the z3c.form base class, z3c.form.form.EditForm, and plone.autoform.form.AutoExtensibleForm, which adds some convenient methods to organize the form fields and widgets. Besides some basic properties such as label and description, the more interesting properties are schema and ignoreContext.

Configure the form#

In this section, you will configure the form's properties.

schema#

The schema property points to a schema interface, which defines the fields of our form.

schema = IMyForm

ignoreContext#

If your form is not bound to an object (such as a Dexterity object), set ignoreContext = True.

Controlling form presentation#

Directives can be specified in the schema to control aspects of form presentation.

Control field and widget presentation#

See the corresponding chapters to learn how to control field and widget presentation in a form.

Display Forms#

Sometimes rather than rendering a form for data entry, you want to display stored values based on the same schema. This can be done using a "display form". The display form renders each field's widget in "display mode", which means that it shows the field value in read-only form rather than as a form input.

To use the display form, create a view that extends WidgetsView like this:

from plone.autoform.view import WidgetsView

class MyView(WidgetsView):
    schema = IMySchema
    additionalSchemata = (ISchemaOne, ISchemaTwo,)

To render the form, do not override __call__(). Instead, either implement the render() method, set an index attribute to a page template or other callable, or use the template attribute of the <browser:page /> ZCML directive when registering the view.

In the template, you can use the following variables:

  • view/w is a dictionary of all widgets, including those from non-default fieldsets. By contrast, the widgets variable contains only those widgets in the default fieldset. The keys are the field names, and the values are widget instances. To render a widget (in display mode), you can do tal:replace="structure view/w/myfield/render" />.

  • view/fieldsets is a dictionary of all fieldsets not including the default fieldset, in other words, those widgets not placed into a fieldset. The keys are the fieldset names, and the values are the fieldset form instances, which in turn have variables, such as widgets, given a list of all widgets.

Dexterity add and edit forms#

Dexterity content types come with default add and edit forms. You can build custom add and edit forms to adjust their behavior.

The implementation of the default edit and add forms is in plone.dexterity.browser.edit.py and plone.dexterity.browser.add.py.

Todo

Describe Add/Edit forms here and how to customize them. Provide mutiple examples.

Disable form tabbing#

To disable the form tabbing, you have to override the edit and add forms, and provide a property enable_form_tabbing as False.

The Python code custom_forms.py should look like this:

from plone.dexterity.browser import add
from plone.dexterity.browser import edit
from zope.interface import implementer
from zope.interface import Interface


class ICustomEditForm(Interface):
    """
    """

@implementer(ICustomEditForm)
class CustomEditForm(edit.DefaultEditForm):
    """ Custom edit form disabling form_tabbing
    """
    enable_form_tabbing = False


class ICustomAddForm(Interface):
    """
    """


@implementer(ICustomAddForm)
class CustomAddForm(add.DefaultAddForm):
    """ Custom add form disabling form_tabbing
    """
    enable_form_tabbing = False


class ICustomAddView(Interface):
    """ """


@implementer(ICustomAddView)
class CustomAddView(add.DefaultAddView):
    form = CustomAddForm

To activate them, you must override the registration of the forms for your desired content types. In your configure.zcml:

<configure
    xmlns="http://namespaces.zope.org/zope"
    xmlns:browser="http://namespaces.zope.org/browser"
    i18n_domain="example.contenttypes">

  <!-- Edit form -->
  <browser:page
    name="edit"
    for="example.contenttypes.content.technical_facility.ITechnicalFacility"
    class=".custom_forms.CustomEditForm"
    permission="cmf.ModifyPortalContent"
    layer="example.contenttypes.interfaces.IExampleContenttypesLayer"
    />

  <!-- Edit form for TTW CT's -->
  <browser:page
    name="edit"
    for="plone.dexterity.interfaces.IDexterityContainer"
    class=".custom_forms.CustomEditForm"
    permission="cmf.ModifyPortalContent"
    layer="example.contenttypes.interfaces.IExampleContenttypesLayer"
    />

  <!-- Add form  -->
  <adapter
      factory=".custom_forms.CustomAddView"
      provides="zope.publisher.interfaces.browser.IBrowserPage"
      for="Products.CMFCore.interfaces.IFolderish
           example.contenttypes.interfaces.IExampleContenttypesLayer
           plone.dexterity.interfaces.IDexterityFTI"
      name="Technical Facility"
      />

  <class class=".custom_forms.CustomAddView">
    <require
        permission="cmf.AddPortalContent"
        interface="zope.publisher.interfaces.browser.IBrowserPage"
        />
  </class>

</configure>