[Product-Developers] Re: Using plone.portlets to define portlets by interface, not by type

Martin Aspeli optilude at gmx.net
Sun Feb 3 19:15:39 UTC 2008


Hi Tim,


> Ok, so I've tried to override the standard IPortletContext with my own 
> that additionally returns an 'interface_category' category.  It looks 
> (approximately) like this:
> 
> class WeblogAwarePortletContext(ContentContext):
>      """
>      """
> 
>      implements(IPortletContext)
>      adapts(Interface, ISiteRoot)
> 
>      def globalPortletCategories(self, placeless=False):
>          cats = super(ContentContext, 
> self).globalPortletCategories(placeless)
>          ifaces = self._getInterfaces()
>          for iface in ifaces:
>              cats.append(INTERFACE_CATEGORY, iface.__identifier__)
> 
>      def _getInterfaces(self):
>          if IWeblog.providedBy(self.context):
>              return [IWeblog,]
>          if IWeblogEnhanced.providedBy(self.context):
>              return [IWeblogEnhanced,]
>          return [Interface,]

Looks about right.

> In my overrides.zcml file (which is explicitly invoked from an 
> overrides.zcml in my Products.QuillsEnabled package), I have the following:
> 
> <configure xmlns="http://namespaces.zope.org/zope"
>             xmlns:browser="http://namespaces.zope.org/browser"
>             xmlns:plone="http://namespaces.plone.org/plone">
> 
>    <!-- Set up the portlet context -->
>    <adapter factory=".context.WeblogAwarePortletContext" />
> 
> </configure>

I'm not sure I'd have it in overrides.zcml. I'd just make it a 
more-specific adapter, e.g. an adapter for a marker interface that you 
apply to whatever context. Your adaptation of ISiteRoot looks odd here; 
I would've though you just wanted to adapt IQuillsContent or whatever, 
or - if you want a very generic one - write an adapter for a custom 
marker interface and use the <class ...><implements ... /></class> ZCML 
construct to apply that marker to a base class like CMF's DynamicContent.

Why? Because overrides are a blunt instrument that can only be used once.

> However, when I try to assign some specific portlets to this category, I 
> get a KeyError on my value for INTERFACE_CATEGORY (i.e. 
> 'interface_category').  Specifically, I try:
> 
> def weblogPortletSetup(portal,
>                         out,
>                         ifaces=[IWeblog, IWeblogEnhanced]):
>      left_column = getUtility(IPortletManager, name="plone.leftcolumn")
>      left_category = left_column[INTERFACE_CATEGORY]
> 
> ... and get the KeyError on that last line.

Of course. Nothing so far has done left_column[INTERFACE_CATEGORY] = 
PortletAssignmentMapping() or whatever. :) You need to do that in your 
setup code, or alternatively do it lazily on traversal. IIRC, 
plone.app.portlets' GS handlers add the standard categories for new 
portlet managers. That could probably be generalised better.

>> Another thing you may want to think about is to use a more global 
>> portlet assignment category, but to register different 
>> IPortletRenderer's for different context interfaces. That way, you can 
>> use the same assignment but vary the rendering by interface (including 
>> local markers, of course).
> 
> IIUC, this is the approach that I am taking.  I trying to register a 
> general IPortletContext that gives a new 'interface_category' category. 

You're getting confused. Your approach is definitely valid, but I was 
talking about something a bit different - rendering the same portlets 
(assigned using the existing mechanisms - by context, group, or content 
type) differently depending on the exact context. That is, the renderer 
for the navtree when the context is an IQuillsWeblog or whatever is 
different to the standard renderer.

>   Then I use that same IPortletContext to decide which interfaces are 
> relevant for the portlet lookup (i.e. with my _getInterfaces method). 
> I'm not sure if this is really the best way of approaching this, however.

I think it's a valid approach. This means that specific portlets are 
automatically attached when a specific (marker?) interface is in play.

> Would it make sene for the core plone.app.portlets code to provide an 
> INTERFACE_CATEGORY ootb?  That way, there could be a simple api for 
> registering portlets per interface.  Something like:
> 
> from plone.app.portlets import registerPortletForInterface
> registerPortletForInterface(a_portlet_assignment, IMyInterface)
> 
> Based on those registrations, code in plone.app.portlets would then 
> return a list of portlet assignments for each interface implemented by 
> the context object.

I like the idea - it'd be worth a PLIP, though it's too late for 3.1 of 
course. Obviously, we couldn't have a sensible, generalised TTW UI for 
this (which is why we did it by portal_type, not interface in the first 
place), but for programmers this'd be nice. With the Plone 3.1 GS 
improvements, this would actually "just work" for you to assign a new 
portlet to an interface... I think. ;-)

It doesn't have to be in plone.app.portlets, though. You could write a 
small package that provided the override IPortletContext and wired up 
the new category, and just make Quills depend on that category.

Martin

-- 
Author of `Professional Plone Development`, a book for developers who
want to work with Plone. See http://martinaspeli.net/plone-book





More information about the Product-Developers mailing list