Skip to content

Instantly share code, notes, and snippets.

@djowett
Last active August 29, 2015 14:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save djowett/3b7f40318d01f10db670 to your computer and use it in GitHub Desktop.
Save djowett/3b7f40318d01f10db670 to your computer and use it in GitHub Desktop.
z3cform portlet example
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:plone="http://namespaces.plone.org/plone"
xmlns:i18n="http://namespaces.zope.org/i18n"
xmlns:five="http://namespaces.zope.org/five"
i18n_domain="your.package">
<include package="plone.app.portlets"/>
<!-- I think these lines /could/ also help get rid of a nasty
ComponentLookupError similar to this one http://pastebin.com/ZDkN8P0d
but just when I thought I'd been clever I realised I'd stuck them in
another project (Ho hum)
<include package="plone.app.z3cform" />
<include package="plone.z3cform" />
<include package="z3c.form" />
-->
<plone:portlet
name="your.package.exampleportlet"
interface=".exampleportlet.IExamplePortlet"
assignment=".exampleportlet.Assignment"
view_permission="zope2.View"
edit_permission="cmf.ManagePortal"
renderer=".exampleportlet.Renderer"
addview=".exampleportlet.AddForm"
editview=".exampleportlet.EditForm"
/>
</configure>
<dl class="portlet portletExamplePortlet"
tal:define="portal_url context/@@plone_portal_state/portal_url;"
i18n:domain="your.package">
<dt class="portletHeader">
<span class="portletTopLeft"></span>
<span tal:condition="view/title" tal:content="view/title">
My example portlet
</span>
<span class="portletTopRight"></span>
</dt>
<dd class="portletItem odd">
Something more to add?
</dd>
</dl>
from zope.interface import implements
from plone.portlets.interfaces import IPortletDataProvider
from plone.app.portlets.portlets import base
# NOTE: If you do not define any fields for the portlet configuration schema below
# you can remove comment the following import of 'zope.schema', also see other Notes
# below
from zope import schema
from zope.schema.fieldproperty import FieldProperty
from z3c.form import field
from plone.app.portlets.browser import z3cformhelper
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
# TODO: If you require i18n translation for any of your schema fields below,
# uncomment one of the following to import your package MessageFactory
from your.package import MessageFactory as _
# from your.package import _
class IExamplePortlet(IPortletDataProvider):
"""A portlet
It inherits from IPortletDataProvider because for this portlet, the
data that is being rendered and the portlet assignment itself are the
same.
"""
# TODO: Add any zope.schema fields here to capture portlet configuration
# information.
# NOTE: Alternatively, if there are no settings, remove this field and
# leave this as an empty interface - see also notes around the add form
# and edit form below.
some_field = schema.TextLine(title=_(u"Some field"),
description=_(u"A field to use"),
required=True)
class Assignment(base.Assignment):
"""Portlet assignment.
This is what is actually managed through the portlets UI and associated
with columns.
"""
implements(IExamplePortlet)
# TODO: Set default values for the configurable parameters here
some_field = u"Thingummyjig"
# TODO: Add keyword parameters for configurable parameters here
def __init__(self, some_field="Thingummyjig"):
self.some_field = some_field
@property
def title(self):
"""This property is used to give the title of the portlet in the
"manage portlets" screen.
"""
return "Example Portlet"
class Renderer(base.Renderer):
"""Portlet renderer.
This is registered in configure.zcml. The referenced page template is
rendered, and the implicit variable 'view' will refer to an instance
of this class. Other methods can be added and referenced in the template.
"""
render = ViewPageTemplateFile('exampleportlet.pt')
@property
def title(self):
"""Portlet title"""
return "Important: %s" % self.data.some_field
class AddForm(z3cformhelper.AddForm):
"""Portlet add form.
This is registered in configure.zcml. [(?) The form_fields variable tells
z3c.form which fields to display.] The create() method actually
constructs the assignment that is being added.
"""
fields = field.Fields(IExamplePortlet)
def create(self, data):
return Assignment(**data)
# NOTE: If this portlet does not have any configurable parameters, you
# can use the next AddForm implementation instead of the previous.
# class AddForm(base.NullAddForm):
# """Portlet add form.
# """
# def create(self):
# return Assignment()
# NOTE: If this portlet does not have any configurable parameters, you
# can remove the EditForm class definition and delete the editview
# attribute from the <plone:portlet /> registration in configure.zcml
class EditForm(z3cformhelper.EditForm):
"""Portlet edit form.
This is registered with configure.zcml. [(?) The form_fields variable tells
z3c.form which fields to display.]
"""
fields = field.Fields(IExamplePortlet)
<?xml version="1.0"?>
<portlets
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
i18n:domain="your.package">
<portlet
addview="your.package.exampleportlet"
title="An example Portlet"
description="A portlet which ..."
i18n:attributes="title title_recent_portlet;
description description_recent_portlet">
<for interface="plone.app.portlets.interfaces.IColumn" />
<for interface="plone.app.portlets.interfaces.IDashboard" />
</portlet>
</portlets>
@djowett
Copy link
Author

djowett commented Oct 10, 2014

Portlets with z3c.form

Plone Portlets normally use zope.formlib, so this is a more modern approach
I got started on this by this StackOverflow question which gave some hints, but not a full solution, so I thought I'd outline one here, which I got by piecing together info from various places.

So portlets.xml goes in your profiles/default directory, and the other files can all go in a your/package/portlets directory. The only other change which is required is in your/package/configure.zcml.

I've tweaked the package & field names since having it working, so perhaps I've busted something in the process, so please let me know if it doesn't work out of the box.

@djowett
Copy link
Author

djowett commented Oct 16, 2014

Note using plone.reload with portlets is known to produce the following kind of error, so we just have to be content to restart your Plone when editing portlets code.

...
Module Products.CMFPlone.browser.ploneview, line 309, in have_portlets
Module plone.app.layout.globals.layout, line 75, in have_portlets
Module plone.portlets.manager, line 48, in visible
Module plone.portlets.manager, line 67, in portletsToShow
Module plone.portlets.manager, line 70, in allPortlets
Module plone.memoize.view, line 47, in memogetter
Module plone.portlets.manager, line 108, in _lazyLoadPortlets
Module plone.app.portlets.manager, line 32, in _dataToPortlet
Module zope.component._api, line 109, in getMultiAdapter
ComponentLookupError: ((<ATDocument at /plone4/whats-on/sunday/homepage>, <HTTPRequest, URL=http://localhost:8080/plone4/whats-on/sunday/homepage/document_view>, <Products.Five.metaclass.Plone object at 0x82113d0>, <plone.portlets.manager.PortletManager object at 0x7274398>, <your.package.portlets.thisSundayPortlet.Assignment object at 0x7f2e9c3c1de8>), <InterfaceClass plone.portlets.interfaces.IPortletRenderer>, u'')
> /home/daniel/dev/Plone/Development/buildout-cache/eggs/zope.component-3.9.5-py2.7.egg/zope/component/_api.py(109)getMultiAdapter()
-> raise ComponentLookupError(objects, interface, name)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment