jueves, 24 de abril de 2008

How to register a local utility in Plone 3 and not lose its persistent data when the product is reinstalled

What you are about to read is not a great solution. There are probably better ways of doing this, but I wanted to share the problems I found and how I solved, because it took me some time to find out what was going on, and perhaps this can be useful for somebody. So, actually this isn't a HOWTO, it's a HOWIDID.

I was developing a Plone 3 (3.0.6) product, generated with ArchGenXML 2.0, and my objective was to add a local utility to it, which should store a list of items. Those items should be added or removed from a page accessible from the Control Panel, and that list would be the options of a drop down selection field of a content type.

Martin Aspeli's book "Profesional Plone Development" was of great help. How to create the configuration page in the Control Panel was almost copying and paste one of his examples (declaring the configlet in controlpanel.xml, creating a view that extends ControlPanelForm, declaring the icon in actionicons.xml and registering it as a resource, etc)

The problem was that he uses the componentregistry import step to register the utility. And that looks really nice, but if you install your product with quickinstaller, quickinstaller runs this step, and somehow it remembers that the utility belongs to that product, and when you remove the product, you will lose all your persisted data.

I tried some things I found on the web but nothing worked, so I desisted with component registry.

I just used the good old Extensions/Install.py, to which I was familiar from Plone 2.5, and registered the utility there. Neither the file or the directory existed (I guess because with ArchGenXML 2.0 everything that was done there is now done through GenericSetup) so I created them.

The utility must be registered in the afterInstall method. Otherwise QuickInstaller will still remove the data.

Lets say we have this code in utility.py in our product:
class IMyUtility(Interface):

types = schema.List(title=_(u'My list'),
description=_(u'A list of items'),

class MyUtility(Persistent):

We will write this code en Extensions/Install.py:
def afterInstall(self, reinstall, product):
from Products.MyProduct.utility import IMyUtility, MyUtility
from Products.CMFCore.utils import getToolByName

portal = getToolByName(self,'portal_url').getPortalObject()
sm = portal.getSiteManager()
if not sm.queryUtility(IMyUtility):

That's it. That works fine if we install our product as an independent product. But there is a problem if we install it as a dependency of a policy product. In that case, Quickinstaller will remember that the utility was created during the installation of the policy product, so the persisted data will survive the reinstallation of the product, but not the reinstallation of the policy product.

In that case, I don't know how to proceed. What I did was to leave the registration code commented in the product Install.py and copy it to the Install.py of the policy product. That sucks, because that registration shouldn't be done by the policy product, but I couldn't imagine another solution.

Do you know one? Leave a comment! :)

UPDATE 16/05/08:
Warning! I don't know exactly why, but after working for a while on my product, I did an update to the whole buildout and I got a:

Error Type: AttributeError
Error Value: type object 'IMyUtility' has no attribute '__iro__'

that broke my Plone Site completely, I can't access anything even from the ZMI. There is not much information about this error on Google, and it is not clear if it is because of this method for registering the utility. Probably yes, so use it at your own risk, and always make backups before updating.

2 comentarios:

Anónimo dijo...

The problem that you mention was recently discussed in the Plone core developers mailing list: http://plone.org/support/forums/core#nabble-td2393544

Santiago Bruno dijo...

Interesting discussion, Emanuel. Thanks for the comment ;)