Layout
======

plone.z3cform tries to avoid mixing Acqusition into your forms.  Also,
it tries to separate form templates from layout templates (aka main
template), so that your forms are usable in different contexts,
e.g. in viewlets.

Use the ``wrap_form`` function defined in the ``layout`` module to
create a view that's suitable for registration with a ``browser:page``
directive.

Testing the FormWrapper class::

    >>> from zope.interface import alsoProvides
    >>> from zope.publisher.browser import TestRequest
    >>> from zope.annotation.interfaces import IAttributeAnnotatable
    >>> from z3c.form.interfaces import IFormLayer

    >>> def make_request(form={}):
    ...     request = TestRequest()
    ...     request.form.update(form)
    ...     alsoProvides(request, IFormLayer)
    ...     alsoProvides(request, IAttributeAnnotatable)
    ...     return request

Then we create a simple z3c form::

    >>> from zope import interface, schema
    >>> from z3c.form import form, field, button
    >>> from plone.z3cform.layout import FormWrapper

    >>> class MySchema(interface.Interface):
    ...     age = schema.Int(title=u"Age")

    >>> from z3c.form.interfaces import IFieldsForm
    >>> from zope.interface import implements
    >>> class MyWrappedForm(form.Form):
    ...     implements(IFieldsForm)
    ...     fields = field.Fields(MySchema)
    ...     ignoreContext = True # don't use context to get widget data
    ...
    ...     @button.buttonAndHandler(u'Apply')
    ...     def handleApply(self, action):
    ...         data, errors = self.extractData()
    ...         print data['age'] # ... or do stuff

    >>> class MyFormWrapper(FormWrapper):
    ...     form = MyWrappedForm
    ...     label = u"Please enter your age"

    >>> from zope.component import provideAdapter
    >>> from zope.publisher.interfaces.browser import IBrowserRequest
    >>> from zope.interface import Interface

    >>> provideAdapter(adapts=(Interface, IBrowserRequest),
    ...                provides=Interface,
    ...                factory=MyFormWrapper,
    ...                name=u"test-form")

Let's verify that worked:

    >>> from zope.component import getMultiAdapter
    >>> context = object()
    >>> request = make_request()
    >>> getMultiAdapter((context, request), name=u"test-form")
    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    <MyFormWrapper object ...>

We can also use a function called ``wrap_form`` to wrap our form in a
layout.  We define a custom layout template first:

    >>> import os
    >>> import tempfile
    >>> from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
    >>> fd, path = tempfile.mkstemp()
    >>> f = os.fdopen(fd, 'w')
    >>> f.write("""
    ... <html>Hello, this is your layout speaking:
    ... <h1 tal:content="view/label">View Title</h1>
    ... <div tal:content="structure view/contents"></div>
    ... </html>""")
    >>> f.close()
    >>> layout = ViewPageTemplateFile(path, _prefix='')

Note that the ``_prefix`` argument passed to Five's
ViewPageTemplateFile is unnecessary when used outside of a test.  We
can now make the actual call to ``wrap_form`` and register the view
class it returns.  Normally, you'd register this view class using
ZCML, like with any other view.
    
    >>> from plone.z3cform.layout import wrap_form
    >>> view_class = wrap_form(MyWrappedForm, index=layout, label=u"My label")
    >>> provideAdapter(adapts=(Interface, IBrowserRequest),
    ...                provides=Interface,
    ...                factory=view_class,
    ...                name=u"test-form2")

To satisfy permission checking, we'll wrap the view in a Zope2-ish
object before we render it.  Again, you usually don't have to worry
about this:

    >>> from Acquisition import Implicit
    >>> class Bar(Implicit):
    ...     __allow_access_to_unprotected_subobjects__ = 1
    ...     implements(Interface)
    >>> context = Bar()

    >>> view = getMultiAdapter(
    ...     (context, request), name=u"test-form2").__of__(context)
    >>> print view() # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    <html>Hello, this is your layout speaking:...My label...Age...</html>
    >>> os.unlink(path)
    
Send bad data to the form:

    >>> request = make_request(form={'form.widgets.age': '12.1'})
    >>> from zope.interface import Interface, implements
    >>> formWrapper = getMultiAdapter((context, request), name=u"test-form")
    >>> form = formWrapper.form(context, request)
    >>> form.update()
    >>> data, errors = form.extractData()
    >>> data
    {}
    >>> errors
    (<ValueErrorViewSnippet for ValueError>,)

And then send correct data to the form:

    >>> request = make_request(form={'form.widgets.age': '12'})
    >>> from zope.interface import Interface, implements
    >>> from Acquisition import Implicit
    >>> formWrapper = getMultiAdapter((context, request), name=u"test-form")
    >>> form = formWrapper.form(context, request)
    >>> form.update()
    >>> data, errors = form.extractData()
    >>> data
    {'age': 12}
    >>> errors
    ()



