I'm trying to create a custom widget for use with a zope.schema.Dict field. The keys are automatically pulled from the list of users and the values will be one of a select vocab. I have most of this more or less working, but my issue is when saving the form. I am using a plone.app.registry.browser.controlpanel.RegistryEditForm view, and the schema contains the Dict field in question. I get the following error:
Traceback (innermost last):
Module ZPublisher.Publish, line 60, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 46, in call_object
Module plone.z3cform.layout, line 70, in __call__
Module plone.z3cform.layout, line 54, in update
Module plone.z3cform.fieldsets.extensible, line 59, in update
Module plone.z3cform.patch, line 30, in GroupForm_update
Module z3c.form.group, line 138, in update
Module z3c.form.action, line 99, in execute
Module z3c.form.button, line 315, in __call__
Module z3c.form.button, line 170, in __call__
Module plone.app.registry.browser.controlpanel, line 55, in handleSave
Module z3c.form.group, line 92, in extractData
Module z3c.form.form, line 145, in extractData
Module z3c.form.field, line 301, in extract
Module zope.component.hooks, line 104, in adapter_hook
Module z3c.form.converter, line 79, in FieldWidgetDataConverter
Module zope.component._api, line 120, in queryMultiAdapter
Module zope.component.registry, line 238, in queryMultiAdapter
Module zope.interface.adapter, line 532, in queryMultiAdapter
Module z3c.form.converter, line 71, in __init__
TypeError: Field ``mypackage_dict`` of type ``Dict`` must provide ``IFromUnicode``.
I tried creating a converter but I'm clearly doing something wrong.
widget code:
class MyPackageWidget(Widget):
implementsOnly(IMyPackageWidget)
klass = u'mypackage-widget'
input_template = ViewPageTemplateFile("input.pt")
def render(self):
if self.mode == INPUT_MODE:
return self.input_template()
def users(self):
utility = getUtility(IMyPackageUtility)
frequencies = utility.frequencies()
for usr in plone.api.user.get_users():
user_id = usr.getId()
yield {'id': user_id,
'myvalue': frequencies.get(user_id),
'name': usr.getProperty('fullname') or user_id}
def extract(self, default=NO_VALUE):
field_key = self.name + '.'
myvalues = {}
for key in self.request.keys():
if key.startswith(field_key):
user_id = safe_unicode(key[len(field_key):])
value = self.request.get(key)
myvalues[user_id] = value
return myvalues
def options(self):
for term in frequencies._terms:
yield {'value': term.value,
'content': term.title}
@adapter(IDict, IFormLayer)
@implementer(IFieldWidget)
def MyPackageFieldWidget(field, request):
""" IFieldWidget factory for MyPackageWidget
"""
return FieldWidget(field, MyPackageWidget(request))
class MyPackageDataConverter(BaseDataConverter):
"""Convert between the context and the widget"""
adapts(IDict, IMyPackageWidget)
def toWidgetValue(self, value):
return value
def toFieldValue(self, value):
return value
The converter doesn't actually do anything yet. I'm not sure what it will need to do yet, but I put a stack trace in those methods and it was just never being hit in the first place. I did verify that the extract code formats the data into something that makes sense and should satisfy the field I have defined.
The zcml:
<class class=".widgets.MyPackageWidget">
<require
permission="zope.Public"
interface="my.package.interfaces.widget.IMyPackageWidget"
/>
</class>
<adapter
factory=".widgets.MyPackageDataConverter"
/>
<adapter
factory=".widgets.MyPackageFieldWidget"
provides="z3c.form.interfaces.IFieldWidget"
for="zope.schema.interfaces.IDict
zope.schema.interfaces.IField
my.package.interfaces.widget.IWidgetsLayer"
/>
<z3c:widgetTemplate
mode="input"
widget="my.package.interfaces.widget.IMyPackageWidget"
layer="my.package.interfaces.widget.IWidgetsLayer"
template="input.pt"
/>
</configure>
Excerpt from the plone.supermodel.model.Schema schema
form.widget(member_frequencies=MyPackageFieldWidget)
mypackage_dict = schema.Dict(
title=_(u"Member/subscription"),
key_type=schema.TextLine(),
value_type=schema.Choice(source=setting_options),
)
If I create a plone.directives.form.SchemaForm the value I get back for data['mypackage_dict'] looks fine and I can save it to the registry. I'd like to be able to automate this more using the controlpanel.RegistryEditForm class though, if I can understand what it is trying to convert.
Plone 4.2