Grok is no longer supported in Plone 5.2 (the first Plone to run on Python 3). Every grok statement can be easily expressed with ZCML. This guide shows how to change code to register ZCA components using ZCML instead of grok.
plone.directives depends on grok so we can't use it anymore.
Before:
myform.py
from five import grok
from plone.directives import form
from myproduct.interfaces import MyInterface
class MyForm(form.SchemaForm):
grok.name('myform-name')
grok.require('cmf.ManagePortal')
grok.context(MyInterface)
schema = IMyForm
After:
configure.zcml
<browser:page
for="myproduct.interfaces.MyInterface"
name="myform-name"
permission="cmf.ManagePortal"
class=".my_module.MyForm"
/>
myform.py
from z3c.form import form
from plone.autoform.form import AutoExtensibleForm
class MyForm(AutoExtensibleForm, form.Form):
schema = IMyForm
Before:
from plone.directives import form
form.primary("fieldname")
form.widget(myField=MyWidget)
After:
from plone.supermodel import directives as form
form.primary("fieldname")
form.widget(myField=MyWidget)
Before:
grok.global_adapter(function_name, name="my-function")
After:
<adapter factory=".modulename.function_name" name="my-function" />
Before:
After:
<utility
provides=".interfaces.IUtilityInterface"
name="utility-name"
factory=".modulename.UtilityClass"
/>
Before:
@grok.subscribe(IMyContext, IObjectModifiedEvent)
def function_name(context, event):
pass
After:
<subscriber
for=".interfaces.IMyContext
zope.lifecycleevent.IObjectModifiedEvent"
handler=".modulename.function_name"
/>
Change it to zope.component.provider
:
Before:
from five import grok
@grok.provider
def my_function(context):
pass
After:
from zope.component import provider
@provider
def my_function(context):
pass
Use the class decorator zope.interface.implementer
instead.
Before:
import grok
class InheritedPubVocab(object):
grok.implements(IContextSourceBinder)
pass
After:
from zope.interface import implementer
@implementer(IContextSourceBinder)
class InheritedPubVocab(object):
pass
Remove inheritance from grok.MultiAdapter
and register using ZCML.
Before:
class MyProvider(grok.MultiAdapter, SomeBaseClass):
grok.name(u'provider-name')
grok.adapts(IFirstArg, ISecondArg, IThirdArg)
grok.provides(ISomething)
After:
class MyProvider(SomeBaseClass):
pass
<adapter
for="myproduct.interfaces.IFirstArg
myproduct.interfaces.ISecondArg
myproduct.interfaces.IThirdArg"
provides=".interfaces.ISomething"
factory=".modulename.MyProvider"
/>
Follow the instruction from grok.MultiAdapter
.
Make sure your class has an __init__
method.
If it doesn't, add one that takes a single context
parameter.
After:
class MyAdapter(object):
def __init__(self, context):
self.context = context
Use Products.Five.browser.BrowserView
instead, and change method render
to __call
.
Also invoke the update
method yourself in the __call__
method.
Before:
class MyView(grok.View):
grok.context(IMyInterface)
grok.require('cmf.ManagePortal')
grok.name('view-name')
def update(self):
self.context.do_something()
def render(self):
return "The view was called!"
After:
from Products.Five.browser import BrowserView
class MyView(BrowserView):
def ___call__(self):
self.update()
return "The view was called!"
<browser:page
for=".interfaces.IMyInterface"
name="view-name"
permission="zope2.Public"
class=".views.MyView"
/>
Grok can also implicitly associate a PageTemplate with the view. In this case we need to name the specific template in python code
Before:
grok.templatedir('templates')
class MyGrokView(grok.View):
grok.name(u'my-items')
grok.context(IPloneSiteRoot)
grok.require('zope2.View')
def update(self):
super(MyGrokView, self).update() # This needs to be removed
self.do_stuff()
After:
from Products.Five.browser import BrowserView
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
class MyGrokView(BrowserView):
index = ViewPageTemplateFile("templates/mygrokview.pt")
def __call__(self):
self.update()
return self.index()
def update(self):
self.do_stuff()
<browser:page
for="Products.CMFPlone.interfaces.IPloneSiteRoot"
name="view-name"
permission="zope2.Public"
class=".views.MyGrokView"
/>
One more – important – note: sometimes grok views don't have explicitly declared
grok.template
. In those cases there's an implicit template lookup in{lowercase_module}_templates/{lowercase_class_name}.pt
. The directory may have been overridden by a module levelgrok.templatedir
declaration. These implicit/missing template declarations must be made explicit when doing the conversion, either in zcml or by setting anindex
attribute and__call__
method on the class.