Accessor and Mutator Methods in Dexterity - plone

I'm beginner user of dexterity (about 2 days now). I'm trying to migrate my old content type to dextertiy content in process of migrating website.
Schema defination in classic archetype is like
TextField('script',
searchable=0,
mutator="write",
accessor="__call__",
edit_accessor="document_src",
widget=TextAreaWidget(label="Page Template script",rows=40,cols=40),
How can I redefine in dexterity ? I'm upgrading from Plone 252 to Plone 412.
Regards,

You will have to create a new Dexterity content type from scratch and completely rewrite your Archetype's Schema to a new schema that inherits from plone.directives.form and with the field types form zope.schema.
For more information, see here:
http://plone.org/products/dexterity/documentation/manual/developer-manual/schema-driven-types/referencemanual-all-pages
For example, your Archetype's schema field declaration, will look like something like this in Dexterity:
script = schema.TextLine(
title=_(u"Page Template Script"),
)
Dexterity content types don't get automatic accessors and mutators like Archetypes content types. Instead, you just access the schema field as if it's an attribute.
For example:
script = myfolder.script
If you want to create the same accessors and mutator (like you specified in the Archetypes field), you'll have to create them manually on your Dexterity class.
For example, something like:
class MyFolder(dexterity.Container):
""" """
grok.implements(IMyFolderSchema)
def __call__(self):
return self.script
def edit_accessor(self):
return self.script
def write(self, value):
self.script = value

Related

Extending SearchableText using collective.dexteritytextindexer

I am trying to extend the SearchableText index for my content type.
I have succeeded in getting multiple fields to be included by marking them as indexer:searchable="true" in the model file.
However I can't extend the SearchableText from my type's py as follows:
class IMyBehavior(form.Schema):
dexteritytextindexer.searchable('description')
description = schema.Text(title=u'Precis')
alsoProvides(IMyBehavior, IFormFieldProvider)
class MySearchableTextExtender(object):
adapts(IMyBehavior)
implements(dexteritytextindexer.IDynamicTextIndexExtender)
def __init__(self, context):
self.context = context
def __call__(self):
"""Extend the searchable text with a custom string"""
return 'some more searchable words'
I have to admit, I don't really know how the first class works. Do I have to set the searchable fields in this class to be able to extend the SearchableText in the second?
If I remove all the indexer:searchable="true" from the model, then the SearchableText is just empty.
Is the first class trying to register the schema at the same time? If so what should this look like if it's just extending the SearchableText?
The collective.dexteritytextindexer provides two important features:
As you already achieved, dexteritytextindexer gives you the ability to put values into Plone's SearchableText index. By adding dexteritytextindexer.searchable(FIELDNAME) to your form, the value of the field will appear in the SearchableText. In Archetypes you have the same feature, by adding searchable=True to the field definition.
collective.dexteritytextindexer gives you also the ability to extend the searchableText manually by registering an IDynamicTextIndexExtender adapter. It extends the values from part 1 with the values from your adapter.
I guess the Problem in your case is, that you have missed to register the adapter: https://github.com/collective/collective.dexteritytextindexer#extending-indexed-data
Example:
<adapter
factory=".yourbehavior.MySearchableTextExtender"
provides="collective.dexteritytextindexer.IDynamicTextIndexExtender"
name="IMyBehavior"
/>
Here's a working example:
This code extends the SearchableText of a container with the searchableText of it's children.
IDynamicTextIndexExtender adapter:
https://github.com/4teamwork/ftw.simplelayout/blob/a7d631de3984b8c1747506b9411045fdf83bc908/ftw/simplelayout/indexer.py
Register the adapter with zcml:
https://github.com/4teamwork/ftw.simplelayout/blob/a7d631de3984b8c1747506b9411045fdf83bc908/ftw/simplelayout/behaviors.zcml#L21
And the most important part - test the implementation:
https://github.com/4teamwork/ftw.simplelayout/blob/a7d631de3984b8c1747506b9411045fdf83bc908/ftw/simplelayout/tests/test_indexer.py#L31

How to add the is_folderish attribute to Dexterity objects?

The .is_folderish attribute is used in many places. For example when setting an object as the default view or when activating discussions on an object.
My first question is how to check is an object has that attribute set. I tried using the bin/instance debug with something like this:
>>> app.site.news.is_folderish
...
AttributeError: is_folderish
I imagine that I can't reach attributes on that way because app.site.news is a wrapper to the object that has that attribute.
My second question is how to add that attribute to a new Dexterity object. I suppose I can do it using the code below (but I can't test it before the my first question is resolved).
from zope import schema
from plone.dexterity.content import Item
class IHorse(form.Schema):
...
class Horse(Item):
def __init__(self):
super(Horse, self).__init__(id)
is_folderish = False
But I'm not sure about how both classes could be linked.
You don't need to add is_folderish to your types; it is an index in the catalog, and Dexterity types already have the proper attribute for that index, isPrincipiaFolderish.
If you do need to add attributes to content types, you can either use an event subscriber or create a custom subclass of plone.dexterity.content.Item:
subscribers can listen for the IObjectCreatedEvent event:
from zope.app.container.interfaces import IObjectAddedEvent
#grok.subscribe(IYourDexterityType, IObjectCreatedEvent)
def add_foo_attribute(obj, event):
obj.foo = 'baz'
custom content classes need to be registered in your type XML:
from plone.dexterity.content import Item
class MyItem(Item):
"""A custom content class"""
...
then in your Dexterity FTI XML file, add a klass property:
<property name="klass">my.package.myitem.MyItem</property>

datagridfield not visible in dexterity through-the-web content type editor

I've added collective.z3cform.datagridfield to my buildout, see it as active in my site settings; however, I cannot add a field of type datagridfield via the through-the-web editor for a dexterity content type. What am I missing?
Extending vangheem's answer: You can provide support for collective.z3cform.datagridfield by providing a field factory, but it will be a hack.
Reason being is, that the collective.z3cform.datagridfield.row.DictRow expects a schema, defining the table rows. This becomes a subform once rendered. The schemaeditor in this instance would need to ask you depending on the field type also for the (table-) schema.
Depending on what solution you are after, you might be able to get away by implementing a field factory with a fixed table schema like this:
from five import grok
from zope import schema
import collective.z3cform.datagridfield.row
import plone.schemaeditor.interfaces
import zope.interface
# example from http://pypi.python.org/pypi/collective.z3cform.datagridfield
class ITableRowSchema(zope.interface.Interface):
one = schema.TextLine(title=u"One")
two = schema.TextLine(title=u"Two")
three = schema.TextLine(title=u"Three")
# new field factory for the zope.schema.interfaces.IObject
class DataGridFieldFactory(grok.GlobalUtility):
grok.provides(plone.schemaeditor.interfaces.IFieldFactory)
# this will show up in the schema editor vocabulary
title = "DataGridField"
def __call__(self, *args, **kwargs):
# that's the horrid part as it will nail your field to this
# specific schema
kw = dict(value_type=collective.z3cform.datagridfield.row.DictRow(
schema=ITableRowSchema))
kwargs.update(kw)
return zope.schema.List(*args, **kwargs)
Please have a look into: plone.schemaeditor.fields.py for more information about the field factory.
This will get you a basic datagrid for your content type. What's missing is the widget, which you currently can't be declared AFAIK.

How to access z3c.form widget settings from browser view

Given following widget based on z3c.form https://github.com/collective/Products.UserAndGroupSelectionWidget/blob/z3cform-widget/src/Products/UserAndGroupSelectionWidget/z3cform/widget.py
I would like in some browserview to access its settings and corresponding field. Since Widget does not know the schema and field upfront, I'm interested in what information do I need to get widget and field. Currently I have available the fieldname and context, which seemed to be enough for archtypes https://github.com/collective/Products.UserAndGroupSelectionWidget/blob/z3cform-widget/src/Products/UserAndGroupSelectionWidget/browser.py#L60
EDIT: To simplify the question, I would like to access a field that is defined in some z3c form and its widget. I could not find other way except passing request and context to form init and then accessing the field. Is there a multiadapter?
The idea is to have a z3c.form widget that people hook into whatever field which does an ajax call. That ajax request needs to pass parameters and response will lookup where widget was used and with what settings. The question is, how to lookup the z3c.form field and which information is needed to do so?
Getting the Field
If you can get the schema, you can get the field.
For a dexterity content type, if you know the field name and the type's portal_type, you can get the schema from the type's Factory Type Information (FTI).
So, if we know portal_type and field_name:
from zope.component import getUtility
from plone.dexterity.interfaces import IDexterityFTI
fti = getUtility(IDexterityFTI, name=portal_type)
schema = fti.lookupSchema()
field = schema.get(field_name)
Getting the Widget
From the z3c.form documentation: http://packages.python.org/z3c.form/widget.html
The widget is a multiadapter, so if you have the field, you can get it like so:
ageWidget = zope.component.getMultiAdapter((field, request),
interfaces.IFieldWidget)
Important: If you have however specified a widget via plone.autoform, then that widget won't be fetched. plone.autoform manually sets a widgetFactory on the z3c.form.field.Field object (which is not the same as the zope.schema Field!). The best way to get the widget then, is what you have already done, by manually calling initiating a FieldWidget.
So for example if you want the UserAndGroupSelectionWidget:
widget = FieldWidget(field, UserAndGroupSelectionWidget(field, request))
P.S Since I'm also in the collective and use the picker widget, I've updated the code for you ;)

How to check constraints on dexterity content types fields

I want to check during edit form saving process the value of a field verify some constraints
(understand calling a method where I can invalidate the form action)
The field must be defined via schema (not supermodel), otherwise the field isn't visible in the schema. Once the field is defined in the schema, you may use a decorated function like the following to set a field validator:
#form.validator(field=IMySchema['title'])
def validateTitle(value):
if value == value.upper():
raise schema.ValidationError(u"Please don't shout")
I'm pretty sure you can do this with a filesystem code dexterity type using zope.interface invariants.
Take a look at the Dexterity Developer Manual, on the chapter dedicated to validators.

Resources