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

Tim Hicks tim at sitefusion.co.uk
Sun Feb 3 17:47:25 UTC 2008


Hi Martin,

(Re-awakening an old thread...)

Thanks for your reply before.  I'm afraid I'm only just getting back to 
this issue...

Martin Aspeli wrote:
> Tim Hicks wrote:
>> Hi (Martin),
>>
>> Is it possible to assign a selection of portlets to a specific
>> interface, rather than a specific content type?  That is, can I do
>> something like the following, where INTERFACE_CATEGORY has been
>> substituted in for CONTENT_TYPE_CATEGORY?
>>
>>
>>     from plone.portlets.constants import INTERFACE_CATEGORY
>>     left_column = getUtility(IPortletManager, name="plone.leftcolumn")
>>     left_category = left_column[INTERFACE_CATEGORY]
>>     left_portlets = left_category.get('Weblog', None)
>>     # It may be that it hasn't been created yet, so just to be safe:
>>     if left_portlets is None:
>>         left_category[my_interfaces_dotted_name] =
>> PortletAssignmentMapping()
>>         left_portlets = left_category[my_interfaces_dotted_name]
>>     for name, assignment, kwargs in DEFAULT_LEFT_PORTLETS:
>>         if not left_portlets.has_key(name):
>>             left_portlets[name] = assignment(**kwargs)
> 
> An IPortletRetriever is used to pick the portlets to be returned. It 
> asks an IPortletContext what the "current" cateogories and their values 
> are - so, we may return a tuple like ((CONTENT_TYPE_CATEGORY, 
> 'Document',), (GROUP_CATEGORY, 'MyGroup',), (GROUP_CATEGORY, 
> 'MyOtherGroup')). It then uses the category + value (in the order given) 
> to look up global portlet assignments in an IPortletManager.

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,]

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>

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.

One clue about why this isn't working is that adding an "import pdb; 
pdb.set_trace()" to the first line of my globalPortletCategories method 
reveals that it never gets called.  So, it looks like I haven't got 
something registered properly.  Any clues?

> So yes - you can define new categories, and you can use the dict-like 
> semantics of an IPortletManager to store them. The README.txt in 
> plone.portlets (not plone.app.portlets) will tell you more about how 
> this works.

Ok.

> 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. 
  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.

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.

Just a thought.


Tim




More information about the Product-Developers mailing list