It’s easy 🙂 It took me only two years to figure this out.
Below is an example how to render a portlet in Plone programmatically. This is useful when you want to have special page layouts and you need to include portlet output from another part of the site.
- Portlet machinery uses Zope’s adapter pattern extensively. This allows you to override things based on the content context, HTTP request, etc.
- A portlet is assigned to some context in some portlet manager
- We can dig these assignments up by portlet assignment id (not user visible) or portlet type (portlet assignment interface)
- Each portlet has its own overrideable renderer class
This all makes everything flexible, though still not flexible enough for some use cases (blacklisting portlets). The downside is that accessing things through many abstraction layers and plug-in points (adaptions) is little cumbersome.
Here is sample code for digging up a portlet and calling its renderer:
import Acquisition from zope.component import getUtility, getMultiAdapter, queryMultiAdapter from plone.portlets.interfaces import IPortletRetriever, IPortletManager, IPortletRenderer def get_portlet_manager(column): """ Return one of default Plone portlet managers. @param column: "plone.leftcolumn" or "plone.rightcolumn" @return: plone.portlets.interfaces.IPortletManagerRenderer instance """ manager = getUtility(IPortletManager, name=column) return manager def render_portlet(context, request, view, manager, interface): """ Render a portlet defined in external location. .. note :: Portlets can be idenfied by id (not user visible) or interface (portlet class). This method supports look up by interface and will return the first matching portlet with this interface. @param context: Content item reference where portlet appear @param manager: IPortletManagerRenderer instance @param view: Current view or None if not available @param interface: Marker interface class we use to identify the portlet. E.g. IFacebookPortlet @return: Rendered portlet HTML as a string, or empty string if portlet not found """ retriever = getMultiAdapter((context, manager), IPortletRetriever) portlets = retriever.getPortlets() assignment = None for portlet in portlets: # portlet is {'category': 'context', 'assignment': , 'name': u'facebook-like-box', 'key': '/isleofback/sisalto/huvit-ja-harrasteet # Identify portlet by interface provided by assignment if interface.providedBy(portlet["assignment"]): assignment = portlet["assignment"] break if assignment is None: # Did not find a portlet return "" #- A special type of content provider, IPortletRenderer, knows how to render each #type of portlet. The IPortletRenderer should be a multi-adapter from #(context, request, view, portlet manager, data provider). renderer = queryMultiAdapter((context, request, view, manager, assignment), IPortletRenderer) # Make sure we have working acquisition chain renderer = renderer.__of__(context) if renderer is None: raise RuntimeError("No portlet renderer found for portlet assignment:" + str(assignment)) renderer.update() # Does not check visibility here... force render always html = renderer.render() return html
This is how you integrate it to your view class:
def render_slope_info(self): """ Render a portlet from another page in-line to this page Does not render other portlets in the same portlet manager. """ context = self.context.aq_inner request = self.request view = self column = "isleofback.app.frontpageportlets" # Our custom interface marking a portlet from isleofback.app.portlets.slopeinfo import ISlopeInfo manager = get_portlet_manager(column) html = render_portlet(context, request, view, manager, ISlopeInfo) return html
…and this is how you call your view helper method from TAL page template:
<div tal:replace="structure view/render_slope_info" />
Subscribe to RSS feed Follow me on Twitter Follow me on Facebook Follow me Google+