    ===================
    Multi+Object Widget
    ===================

    The multi widget allows you to add and edit one or more values.

    In order to not overwhelm you with our set of well-chosen defaults,
    all the default component registrations have been made prior to doing those
    examples:

      >>> from z3c.form import testing
      >>> testing.setupFormDefaults()

    As for all widgets, the multi widget must provide the new ``IWidget``
    interface:

      >>> from zope.interface.verify import verifyClass
      >>> from z3c.form import interfaces
      >>> from z3c.form.browser import multi

      >>> verifyClass(interfaces.IWidget, multi.MultiWidget)
      True

    The widget can be instantiated only using the request:

      >>> from z3c.form.testing import TestRequest
      >>> request = TestRequest()
      >>> widget = multi.MultiWidget(request)

    Before rendering the widget, one has to set the name and id of the widget:

      >>> widget.id = 'widget-id'
      >>> widget.name = 'widget.name'

    We also need to register the template for at least the widget and request:

      >>> import zope.component
      >>> from zope.pagetemplate.interfaces import IPageTemplate
      >>> from z3c.form.testing import getPath
      >>> from z3c.form.widget import WidgetTemplateFactory

      >>> zope.component.provideAdapter(
      ...     WidgetTemplateFactory(getPath('multi_input.pt'), 'text/html'),
      ...     (None, None, None, None, interfaces.IMultiWidget),
      ...     IPageTemplate, name=interfaces.INPUT_MODE)

      >>> zope.component.provideAdapter(
      ...     WidgetTemplateFactory(getPath('multi_display.pt'), 'text/html'),
      ...     (None, None, None, None, interfaces.IMultiWidget),
      ...     IPageTemplate, name=interfaces.DISPLAY_MODE)

    For the next test, we need to setup our button handler adapters.

      >>> from z3c.form import button
      >>> zope.component.provideAdapter(button.ButtonActions)
      >>> zope.component.provideAdapter(button.ButtonActionHandler)
      >>> zope.component.provideAdapter(button.ButtonAction,
      ...     provides=interfaces.IButtonAction)

    Our submit buttons will need a template as well:

      >>> zope.component.provideAdapter(
      ...     WidgetTemplateFactory(getPath('submit_input.pt'), 'text/html'),
      ...     (None, None, None, None, interfaces.ISubmitWidget),
      ...     IPageTemplate, name=interfaces.INPUT_MODE)

    We can now render the widget:

      >>> widget.update()
      >>> print widget.render()
    As you can see the widget is empty and doesn't provide values. This is because
    the widget does not know what sub-widgets to display. So let's register a
    `IFieldWidget` adapter and a template for our `IInt` field:

      >>> import z3c.form.interfaces
      >>> from z3c.form.browser.text import TextFieldWidget
      >>> zope.component.provideAdapter(TextFieldWidget,
      ...     (zope.schema.interfaces.IInt, z3c.form.interfaces.IFormLayer))

      >>> zope.component.provideAdapter(
      ...     WidgetTemplateFactory(getPath('text_input.pt'), 'text/html'),
      ...     (None, None, None, None, interfaces.ITextWidget),
      ...     IPageTemplate, name=interfaces.INPUT_MODE)

    Let's now update the widget and check it again.

      >>> widget.update()
      >>> print widget.render()
    It's still the same. Since the widget doesn't provide a field nothing useful
    gets rendered. Now let's define a field for this widget and check it again:

      >>> from z3c.form.widget import FieldWidget

      >>> from z3c.form.testing import IMySubObjectMulti
      >>> from z3c.form.testing import MySubObjectMulti

      >>> from z3c.form.object import registerFactoryAdapter
      >>> registerFactoryAdapter(IMySubObjectMulti, MySubObjectMulti)

      >>> field = zope.schema.List(
      ...     __name__='foo',
      ...     value_type=zope.schema.Object(title=u'my object widget',
      ...                                   schema=IMySubObjectMulti),
      ...     )

      >>> widget = FieldWidget(field, widget)
      >>> widget.update()
      >>> print widget.render()
    As you can see, there is still no input value. Let's provide some values for
    this widget. Before we can do that, we will need to register a data converter
    for our multi widget and the data converter dispatcher adapter:

      >>> from z3c.form.converter import IntegerDataConverter
      >>> from z3c.form.converter import FieldWidgetDataConverter
      >>> from z3c.form.converter import MultiConverter
      >>> from z3c.form.validator import SimpleFieldValidator
      >>> zope.component.provideAdapter(IntegerDataConverter)
      >>> zope.component.provideAdapter(FieldWidgetDataConverter)
      >>> zope.component.provideAdapter(SimpleFieldValidator)
      >>> zope.component.provideAdapter(MultiConverter)

    Bunch of adapters to get objectwidget work:

      >>> from z3c.form import datamanager
      >>> zope.component.provideAdapter(datamanager.DictionaryField)

      >>> import z3c.form.browser.object
      >>> zope.component.provideAdapter(z3c.form.browser.object.ObjectFieldWidget)
      >>> import z3c.form.object
      >>> zope.component.provideAdapter(z3c.form.object.ObjectConverter)
      >>> import z3c.form.error
      >>> zope.component.provideAdapter(z3c.form.error.ValueErrorViewSnippet)
      >>> from z3c.form.object import SubformAdapter
      >>> zope.component.provideAdapter(SubformAdapter)

      >>> from zope.pagetemplate.interfaces import IPageTemplate
      >>> from z3c.form.testing import getPath
      >>> from z3c.form.widget import WidgetTemplateFactory

      >>> zope.component.provideAdapter(
      ...     WidgetTemplateFactory(getPath('object_input.pt'), 'text/html'),
      ...     (None, None, None, None, interfaces.IObjectWidget),
      ...     IPageTemplate, name=interfaces.INPUT_MODE)

      >>> zope.component.provideAdapter(
      ...     WidgetTemplateFactory(getPath('object_display.pt'), 'text/html'),
      ...     (None, None, None, None, interfaces.IObjectWidget),
      ...     IPageTemplate, name=interfaces.DISPLAY_MODE)

      >>> widget.update()

    It must not fail if we assign values that do not meet the constraints,
    just cry about it in the HTML:

      >>> widget.value = [{'foofield': None, 'barfield': 666}]
      >>> widget.update()
      >>> print widget.render()
my object widget *
Wrong contained type
My foo field *
Required input is missing.
My dear bar
    Let's set acceptable values:

      >>> widget.value = [dict(foofield=42, barfield=666),
      ...     dict(foofield=789, barfield=321)]

      >>> print widget.render()
my object widget *
My foo field *
My dear bar
my object widget *
My foo field *
My dear bar
Let's see what we get on value extraction: >>> widget.extract()
    If we now click on the ``Add`` button, we will get a new input field for enter
    a new value:

      >>> widget.request = TestRequest(form={'foo.count':u'2',
      ...                                    'foo.0.widgets.foofield':u'42',
      ...                                    'foo.0.widgets.barfield':u'666',
      ...                                    'foo.0-empty-marker':u'1',
      ...                                    'foo.1.widgets.foofield':u'789',
      ...                                    'foo.1.widgets.barfield':u'321',
      ...                                    'foo.1-empty-marker':u'1',
      ...                                    'foo.buttons.add':'Add'})
      >>> widget.update()
      >>> print widget.render()
my object widget *
My foo field *
My dear bar
my object widget *
My foo field *
My dear bar
my object widget *
My foo field *
My dear bar
    Let's see what we get on value extraction:

      >>> value = widget.extract()
      >>> value
      [{'foofield': 42, 'barfield': 666}, {'foofield': 789, 'barfield': 321}]
      >>> converter = interfaces.IDataConverter(widget)

      >>> value = converter.toFieldValue(value)
      >>> value
      [
,
    ]

      >>> value[0].foofield
      42
      >>> value[0].barfield
      666

    Now let's store the new value:

      >>> widget.request = TestRequest(form={'foo.count':u'3',
      ...                                    'foo.0.widgets.foofield':u'42',
      ...                                    'foo.0.widgets.barfield':u'666',
      ...                                    'foo.0-empty-marker':u'1',
      ...                                    'foo.1.widgets.foofield':u'789',
      ...                                    'foo.1.widgets.barfield':u'321',
      ...                                    'foo.1-empty-marker':u'1',
      ...                                    'foo.2.widgets.foofield':u'46',
      ...                                    'foo.2.widgets.barfield':u'98',
      ...                                    'foo.2-empty-marker':u'1',
      ...                                    })
      >>> widget.update()
      >>> print widget.render()
my object widget *
My foo field *
My dear bar
my object widget *
My foo field *
My dear bar
my object widget *
My foo field *
My dear bar
    Let's see what we get on value extraction:

      >>> value = widget.extract()
      >>> value
      [{'foofield': 42, 'barfield': 666},
      {'foofield': 789, 'barfield': 321},
      {'foofield': 46, 'barfield': 98}]
      >>> converter = interfaces.IDataConverter(widget)

      >>> value = converter.toFieldValue(value)
      >>> value
      [
,
    ]

      >>> value[0].foofield
      42
      >>> value[0].barfield
      666

    As you can see in the above sample, the new stored value gets rendered as a
    real value and the new adding value input field is gone. Now let's try to
    remove an existing value:

      >>> widget.request = TestRequest(form={'foo.count':u'3',
      ...                                    'foo.0.widgets.foofield':u'42',
      ...                                    'foo.0.widgets.barfield':u'666',
      ...                                    'foo.0-empty-marker':u'1',
      ...                                    'foo.1.widgets.foofield':u'789',
      ...                                    'foo.1.widgets.barfield':u'321',
      ...                                    'foo.1-empty-marker':u'1',
      ...                                    'foo.2.widgets.foofield':u'46',
      ...                                    'foo.2.widgets.barfield':u'98',
      ...                                    'foo.2-empty-marker':u'1',
      ...                                    'foo.1.remove':u'1',
      ...                                    'foo.buttons.remove':'Remove selected'})
      >>> widget.update()
      >>> print widget.render()
my object widget *
My foo field *
My dear bar
my object widget *
My foo field *
My dear bar
    Let's see what we get on value extraction:
    (this is good so, because Remove selected is a widget-internal submit)

      >>> value = widget.extract()
      >>> value
      [{'foofield': 42, 'barfield': 666},
      {'foofield': 789, 'barfield': 321},
      {'foofield': 46, 'barfield': 98}]
      >>> converter = interfaces.IDataConverter(widget)

      >>> value = converter.toFieldValue(value)
      >>> value
      [
,
    ]

      >>> value[0].foofield
      42
      >>> value[0].barfield
      666

    Error handling is next. Let's use the value "bad" (an invalid integer literal)
    as input for our internal (sub) widget.

      >>> from z3c.form.error import ErrorViewSnippet
      >>> from z3c.form.error import StandardErrorViewTemplate
      >>> zope.component.provideAdapter(ErrorViewSnippet)
      >>> zope.component.provideAdapter(StandardErrorViewTemplate)

      >>> widget.request = TestRequest(form={'foo.count':u'2',
      ...                                    'foo.0.widgets.foofield':u'42',
      ...                                    'foo.0.widgets.barfield':u'666',
      ...                                    'foo.0-empty-marker':u'1',
      ...                                    'foo.1.widgets.foofield':u'bad',
      ...                                    'foo.1.widgets.barfield':u'98',
      ...                                    'foo.1-empty-marker':u'1',
      ...                                    })

      >>> widget.update()
      >>> print widget.render()
my object widget *
My foo field *
My dear bar
my object widget *
Object is of wrong type.
My foo field *
The entered value is not a valid integer literal.
My dear bar
    Let's see what we get on value extraction:

      >>> value = widget.extract()
      >>> value
      [{'foofield': 42, 'barfield': 666},
      {'foofield': u'bad', 'barfield': u'98'}]

    Label
    -----

    There is an option which allows to disable the label for the (sub) widgets.
    You can set the `showLabel` option to `False` which will skip rendering the
    labels. Alternatively you can also register your own template for your layer
    if you like to skip the label rendering for all widgets.

      >>> field = zope.schema.List(
      ...     __name__='foo',
      ...     value_type=zope.schema.Object(title=u'ignored_title',
      ...                                   schema=IMySubObjectMulti),
      ...     )
      >>> request = TestRequest()
      >>> widget = multi.MultiWidget(request)
      >>> widget = FieldWidget(field, widget)
      >>> widget.value = [dict(foofield=42, barfield=666),
      ...     dict(foofield=789, barfield=321)]
      >>> widget.showLabel = False
      >>> widget.update()
      >>> print widget.render()
My foo field *
My dear bar
My foo field *
My dear bar
    In a form
    ---------

    Let's try a simple example in a form.

    We have to provide an adapter first:

      >>> import z3c.form.browser.object
      >>> zope.component.provideAdapter(z3c.form.browser.object.ObjectFieldWidget)

    Forms and our objectwidget fire events on add and edit, setup a subscriber
    for those:

      >>> eventlog = []
      >>> import zope.lifecycleevent
      >>> @zope.component.adapter(zope.lifecycleevent.ObjectModifiedEvent)
      ... def logEvent(event):
      ...     eventlog.append(event)
      >>> zope.component.provideHandler(logEvent)
      >>> @zope.component.adapter(zope.lifecycleevent.ObjectCreatedEvent)
      ... def logEvent2(event):
      ...     eventlog.append(event)
      >>> zope.component.provideHandler(logEvent2)

      >>> def printEvents():
      ...     for event in eventlog:
      ...         print str(event)
      ...         if isinstance(event, zope.lifecycleevent.ObjectModifiedEvent):
      ...             for attr in event.descriptions:
      ...                 print attr.interface
      ...                 print attr.attributes

    We need to provide the widgets for the List

      >>> from z3c.form.browser.multi import multiFieldWidgetFactory
      >>> zope.component.provideAdapter(multiFieldWidgetFactory,
      ...     (zope.schema.interfaces.IList, z3c.form.interfaces.IFormLayer))
      >>> zope.component.provideAdapter(multiFieldWidgetFactory,
      ...     (zope.schema.interfaces.ITuple, z3c.form.interfaces.IFormLayer))

    We define an interface containing a subobject, and an addform for it:

      >>> from z3c.form import form, field
      >>> from z3c.form.testing import MyMultiObject, IMyMultiObject

    Note, that creating an object will print some information about it:

      >>> class MyAddForm(form.AddForm):
      ...     fields = field.Fields(IMyMultiObject)
      ...     def create(self, data):
      ...         print "MyAddForm.create", str(data)
      ...         return MyMultiObject(**data)
      ...     def add(self, obj):
      ...         self.context[obj.name] = obj
      ...     def nextURL(self):
      ...         pass

    We create the form and try to update it:

      >>> request = TestRequest()
      >>> myaddform =  MyAddForm(root, request)

      >>> myaddform.update()

    As usual, the form contains a widget manager with the expected widget

      >>> myaddform.widgets.keys()
      ['listOfObject', 'name']
      >>> myaddform.widgets.values()
      [
,
    ]

    If we want to render the addform, we must give it a template:

      >>> import os
      >>> from zope.app.pagetemplate import viewpagetemplatefile
      >>> from z3c.form import tests
      >>> def addTemplate(form):
      ...     form.template = viewpagetemplatefile.BoundPageTemplate(
      ...         viewpagetemplatefile.ViewPageTemplateFile(
      ...             'simple_edit.pt', os.path.dirname(tests.__file__)), form)
      >>> addTemplate(myaddform)

    Now rendering the addform renders no items yet:

      >>> print myaddform.render()
My list field
name
    We don't have the object (yet) in the root:

      >>> root['first']
      Traceback (most recent call last):
      ...
      KeyError: 'first'

    Add a row to the multi widget:

      >>> request = TestRequest(form={
      ...     'form.widgets.listOfObject.count':u'0',
      ...     'form.widgets.listOfObject.buttons.add':'Add'})
      >>> myaddform.request = request

    Update with the request:

      >>> myaddform.update()

    Render the form:

      >>> print myaddform.render()
My list field
my object widget *
My foo field *
My dear bar
name
    Now we can fill in some values to the object, and a name to the whole schema:

      >>> request = TestRequest(form={
      ...     'form.widgets.listOfObject.count':u'1',
      ...     'form.widgets.listOfObject.0.widgets.foofield':u'66',
      ...     'form.widgets.listOfObject.0.widgets.barfield':u'99',
      ...     'form.widgets.listOfObject.0-empty-marker':u'1',
      ...     'form.widgets.name':u'first',
      ...     'form.buttons.add':'Add'})
      >>> myaddform.request = request

    Update the form with the request:

      >>> myaddform.update()
      MyAddForm.create {'name': u'first',
      'listOfObject': [
]} Wow, it got added: >>> root['first'] >>> root['first'].listOfObject [
    ]

    Field values need to be right:

      >>> root['first'].listOfObject[0].foofield
      66
      >>> root['first'].listOfObject[0].barfield
      99

    Let's see our event log:

      >>> len(eventlog)
      6

    ((why is IMySubObjectMulti created twice???))

      >>> printEvents()
('foofield', 'barfield') ('foofield', 'barfield')
      >>> eventlog=[]

    Let's try to edit that newly added object:

      >>> class MyEditForm(form.EditForm):
      ...     fields = field.Fields(IMyMultiObject)

      >>> editform = MyEditForm(root['first'], TestRequest())
      >>> addTemplate(editform)
      >>> editform.update()

    Watch for the widget values in the HTML:

      >>> print editform.render()
My list field
my object widget *
My foo field *
My dear bar
name
    Let's modify the values:

      >>> request = TestRequest(form={
      ...     'form.widgets.listOfObject.count':u'1',
      ...     'form.widgets.listOfObject.0.widgets.foofield':u'43',
      ...     'form.widgets.listOfObject.0.widgets.barfield':u'55',
      ...     'form.widgets.listOfObject.0-empty-marker':u'1',
      ...     'form.widgets.name':u'first',
      ...     'form.buttons.apply':'Apply'})

    They are still the same:

      >>> root['first'].listOfObject[0].foofield
      66
      >>> root['first'].listOfObject[0].barfield
      99

      >>> editform.request = request
      >>> editform.update()

    Until we have updated the form:

      >>> root['first'].listOfObject[0].foofield
      43
      >>> root['first'].listOfObject[0].barfield
      55

    Let's see our event log:

      >>> len(eventlog)
      7

    ((TODO: now this is real crap here, why is IMySubObjectMulti created 3 times???))

      >>> printEvents()
('foofield', 'barfield') ('foofield', 'barfield') ('foofield', 'barfield') ('listOfObject',) >>> eventlog=[] After the update the form says that the values got updated and renders the new values: >>> print editform.render()
Data successfully updated.
My list field
my object widget *
My foo field *
My dear bar
name
    Let's see if the widget keeps the old object on editing:

    We add a special property to keep track of the object:

      >>> root['first'].listOfObject[0].__marker__ = "ThisMustStayTheSame"

      >>> root['first'].listOfObject[0].foofield
      43
      >>> root['first'].listOfObject[0].barfield
      55

    Let's modify the values:

      >>> request = TestRequest(form={
      ...     'form.widgets.listOfObject.count':u'1',
      ...     'form.widgets.listOfObject.0.widgets.foofield':u'666',
      ...     'form.widgets.listOfObject.0.widgets.barfield':u'999',
      ...     'form.widgets.listOfObject.0-empty-marker':u'1',
      ...     'form.widgets.name':u'first',
      ...     'form.buttons.apply':'Apply'})

      >>> editform.request = request

      >>> editform.update()

    Let's check what are ther esults of the update:

      >>> root['first'].listOfObject[0].foofield
      666
      >>> root['first'].listOfObject[0].barfield
      999

    ((TODO: bummer... we can't keep the old object))

      #>>> root['first'].listOfObject[0].__marker__
      #'ThisMustStayTheSame'

    Let's make a nasty error, by typing 'bad' instead of an integer:

      >>> request = TestRequest(form={
      ...     'form.widgets.listOfObject.count':u'1',
      ...     'form.widgets.listOfObject.0.widgets.foofield':u'99',
      ...     'form.widgets.listOfObject.0.widgets.barfield':u'bad',
      ...     'form.widgets.listOfObject.0-empty-marker':u'1',
      ...     'form.widgets.name':u'first',
      ...     'form.buttons.apply':'Apply'})

      >>> editform.request = request
      >>> eventlog=[]
      >>> editform.update()

    Eventlog must be clean:

      >>> len(eventlog)
      2

    ((TODO: bummer... who creates those 2 objects???))

      >>> printEvents()
Watch for the error message in the HTML: it has to appear at the field itself and at the top of the form: ((not nice: at the top ``Object is of wrong type.`` appears)) >>> print editform.render()
There were some errors.

- My list field:
  Object is of wrong type.

Object is of wrong type.
My list field
my object widget *
Object is of wrong type.
My foo field *
My dear bar
The entered value is not a valid integer literal.
name
    The object values must stay at the old ones:

      >>> root['first'].listOfObject[0].foofield
      666
      >>> root['first'].listOfObject[0].barfield
      999

    Simple but often used use-case is the display form:

      >>> editform = MyEditForm(root['first'], TestRequest())
      >>> addTemplate(editform)
      >>> editform.mode = interfaces.DISPLAY_MODE
      >>> editform.update()
      >>> print editform.render()
My list field
my object widget *
My foo field *
666
My dear bar
999
name first