======================
Sub-form setup details
======================

PLEASE NOTE: This doctest tests ObjectSubForm generation. The more general use-case
is tested in autoform.txt, which also contains a more thorough explanation of
the concepts and mechanisms concerned.

To handle hints for z3c.form.object.ObjectSubForm forms, the special base class
AutoObjectSubForm is required, to set up the 'fields' property on the subform
instance.

We now import the keys under which the tagged values will be stored.

    >>> from plone.autoform.interfaces import OMITTED_KEY
    >>> from plone.autoform.interfaces import WIDGETS_KEY
    >>> from plone.autoform.interfaces import MODES_KEY
    >>> from plone.autoform.interfaces import ORDER_KEY

For the purposes of this test, we'll set the form data manually. See
autoform.txt for more info on how the data could be set.

Test setup
----------

First, let's load this package's ZCML so that we can run the tests:
    >>> configuration = """\
    ... <configure xmlns="http://namespaces.zope.org/zope">
    ...
    ...     <include package="Products.Five" file="configure.zcml" />
    ...     <include package="plone.autoform" />
    ...
    ... </configure>
    ... """
    >>> from StringIO import StringIO
    >>> from zope.configuration import xmlconfig
    >>> xmlconfig.xmlconfig(StringIO(configuration))

We need a sample Schema with an Object field. The Object field will have it's
own schema, ITestSubObjectSchema, which will be the schema for the
ObjectSubForm.

    >>> from zope.interface import Interface
    >>> import zope.schema

    >>> vocab = zope.schema.vocabulary.SimpleVocabulary.fromValues([1, 2])

    >>> class ITestSubObjectSchema(Interface):
    ...     """ This is the schema for the Object field.
    ...
    ...         It will be used to render the ObjectSubForm.
    ...     """
    ...     foofield = zope.schema.TextLine(title=u"Foo")
    ...     barfield = zope.schema.Choice(title=u"Bar", vocabulary=vocab)
    ...     bazfield = zope.schema.Int(title=u"Baz")
    ...     quxfield = zope.schema.TextLine(title=u"Qux")

    >>> class ITestSchema(Interface):
    ...     """ This is the form schema that contains the Object field.
    ...     """
    ...     subobject = zope.schema.Object(
    ...                     title=u'my object widget',
    ...                     schema=ITestSubObjectSchema)

We need a test context and request, marked with the ``IFormLayer`` interface to
make z3c.form happy:

    >>> from zope.publisher.browser import TestRequest
    >>> from z3c.form import interfaces
    >>> context = object()
    >>> request = TestRequest(environ={'AUTHENTICATED_USER': 'user1'}, skin=interfaces.IFormLayer)

Note that we need to pretend that we have authenticated a user. Without this,
the permission checks will be turned off.

Now we define a TestForm, and a TestObjectSubForm. The object subform must
subclass from AutoObjectSubForm for the form hints to be generated.

    >>> from z3c.form.object import ObjectSubForm
    >>> from plone.autoform.form import AutoExtensibleForm
    >>> from plone.autoform.form import AutoObjectSubForm
    >>> from z3c.form import form
    >>> from z3c.form.interfaces import IForm

    >>> class TestForm(AutoExtensibleForm, form.Form):
    ...     schema = ITestSchema
    ...     additionalSchemata = ()
    ...     ignoreContext = True

    >>> TestForm.mode
    'input'

    >>> class TestObjectSubForm(AutoObjectSubForm, ObjectSubForm):
    ...     pass


Finally we need an adapter that acts as the ObjectSubForm's factory.

    >>> from z3c.form.browser.object import ObjectWidget
    >>> from z3c.form.object import SubformAdapter
    >>> import zope.component

    >>> @zope.interface.implementer(interfaces.ISubformFactory)
    ... @zope.component.adapter(zope.interface.Interface,  # widget value
    ...                         interfaces.IFormLayer,       # request
    ...                         zope.interface.Interface,    # widget context
    ...                         zope.interface.Interface,    # form
    ...                         ObjectWidget,                # widget
    ...                         zope.interface.Interface,    # field
    ...                         zope.interface.Interface)    # field.schema
    ... class TestSubformAdapter(SubformAdapter):
    ...     """ """
    ...     factory = TestObjectSubForm

    >>> zope.component.provideAdapter(TestSubformAdapter)


Now we can set some form data on the ObjectSubForm.

Omitted fields are listed like this:

    >>> ITestSubObjectSchema.setTaggedValue(OMITTED_KEY,
    ...                            ((IForm, 'quxfield', True),
    ...                             (IForm, 'barfield', False),
    ...                             (IForm, 'foofield', False),
    ...                           ))

Widgets can be specified either by a dotted name string or an actual instance:

    >>> from z3c.form.browser.radio import RadioFieldWidget
    >>> ITestSubObjectSchema.setTaggedValue(WIDGETS_KEY, {'barfield': RadioFieldWidget})

Fields can be moved like this:

    >>> ITestSubObjectSchema.setTaggedValue(ORDER_KEY, [('foofield', 'after', 'barfield')])

Field modes can be set like this:

    >>> ITestSubObjectSchema.setTaggedValue(MODES_KEY,
    ...                            ((Interface, 'foofield', 'display'),
    ...                             (IForm, 'barfield', 'hidden'))
    ...                           )

The ObjectSubForm is not a GroupForm, so it doesn't support Fieldsets. Fieldset
hints would thus be ignored.

Lets test this now.
-------------------

First we instantiate and update the TestForm.

    >>> test_form = TestForm(context, request)
    >>> test_form.update()

Now we instantiate and update the ObjectSubForm for the object in TestForm's schema.

    >>> widget = test_form.widgets.get('subobject')
    >>> from z3c.form.object import makeDummyObject
    >>> test_subform = zope.component.getMultiAdapter(
    ...     (None, request, context, test_form, widget, widget.field, makeDummyObject(ITestSubObjectSchema)),
    ...     interfaces.ISubformFactory)()

    >>> test_subform.update()

Lets check that foofield is after barfield and that quxfield is not shown.

    >>> test_subform.fields.keys()
    ['barfield', 'foofield', 'bazfield']

And that the barfield has a RadioWidget.

    >>> test_subform.widgets['barfield']
    <RadioWidget 'form.widgets.subobject.widgets.barfield'>

Now the modes, barfield was hidden and foofield was set to display:

    >>> test_subform.widgets['barfield'].mode
    'hidden'
    >>> test_subform.widgets['foofield'].mode
    'display'
