Next I want to add a Portlet which will display any items of the new content type which are in the current folder.
Creating a Plone Portlet
First step seems easy:
cd src/anorakgirl.mycontent paster addcontent portlet
and then answer the easy questions. This creates a some files in the portlets folder of the product. However, when I restart Plone, I get this error:
ZopeXMLConfigurationError: File "c:\plone\src\anorakgirl.mycontent\anorakgirl\mycontent\portlets\configure.zcml", line 12.2-21.8] ImportError: cannot import name QuotesPortletMessageFactory
I fixed this by commenting out the line below from portlets/mycontentportlet.py – who knows if this matters!
#from anorakgirl.mycontent.portlets import MyContentPortletMessageFactory as _
Plone now restarts, and I can add the MyContentPortlet via ‘Manage Portlets’. However, it doesn’t do anything yet.
To make it show the first ‘MyContent’ item from the current folder, I modified portlets/mycontentportlet.py as follows. Please note that this code is shamelessly copied from Professional Plone Development by Martin Aspeli.
from zope import schema from zope.component import getMultiAdapter from zope.formlib import form from zope.interface import implements from plone.app.portlets.portlets import base from plone.memoize.instance import memoize from plone.portlets.interfaces import IPortletDataProvider from DateTime import DateTime from Acquisition import aq_inner from Products.CMFCore.interfaces import ICatalogTool from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile from Products.CMFCore.utils import getToolByName from anorakgirl.mycontent.interfaces import IMyContent #from anorakgirl.mycontent.portlets import MyContentPortletMessageFactory as _ class IMyContentPortlet(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. """ class Assignment(base.Assignment): """Portlet assignment. This is what is actually managed through the portlets UI and associated with columns. """ implements(IMyContentPortlet) def __init__(self): pass @property def title(self): """This property is used to give the title of the portlet in the "manage portlets" screen. """ return "MyContent 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('mycontentportlet.pt') @property def available(self): return len(self._data()) > 0 def mycontent_list(self): for brain in self._data(): mycontent = brain.getObject() yield dict(title=mycontent.title, description=mycontent.description, author=mycontent.author, details=mycontent.details) @memoize def _data(self): context = aq_inner(self.context) limit = 1 query = dict(object_provides = IMyContent.__identifier__) if self.context.isPrincipiaFolderish: query['path'] = '/'.join(context.getPhysicalPath()) if not self.context.isPrincipiaFolderish: parent = context.aq_parent query['path'] = '/'.join(parent.getPhysicalPath())
catalog = getToolByName(context, 'portal_catalog') results = catalog(query) mycontents = [] mycontents = results[:limit] return mycontents # NOTE: If this portlet does not have any configurable parameters, you can # inherit from NullAddForm and remove the form_fields variable. class AddForm(base.AddForm): """Portlet add form. This is registered in configure.zcml. The form_fields variable tells zope.formlib which fields to display. The create() method actually constructs the assignment that is being added. """ form_fields = form.Fields(IMyContentPortlet) def create(self, data): return Assignment(**data) # NOTE: IF this portlet does not have any configurable parameters, you can # remove this class definition and delete the editview attribute from the # <plone:portlet /> registration in configure.zcml class EditForm(base.EditForm): """Portlet edit form. This is registered with configure.zcml. The form_fields variable tells zope.formlib which fields to display. """ form_fields = form.Fields(IMyContentPortlet)
Really all that I changed from the paster generated content was the imports and the Render method.
I also amended the template file mycontentportlet.pt to show the data returned by the Render method:
<dl class="portlet portletMyContentPortlet" i18n:domain="anorakgirl.mycontent.portlets"> <dd class="portletItem odd"> <div tal:define="items view/mycontent_list"> <ul> <li tal:repeat="item items"> <span tal:content="item/details"/> <br/><b tal:content="item/author"></b></li> </ul> </div> </dd> </dl>
Because paster amended the xml files in the profiles folder ( to register the new portlet) not only do you need to restart Plone, you need to reinstall the product to get the Portlet to show up. I’m doing this using the Setup > Add-on products page, but this may not be the best way?
You should now be able to add the Portlet using Manage Portlets. If you add it at the root, and the rest of the site is set to inherit parent portlets, then the portlet should show up in any folder in which you add a ‘MyContent’ item.