sábado, 26 de abril de 2008

BadRequest error running the import steps of a selected profile from a product with several profiles

I had to add an action to a content type named "Attachment" for a Plone 3 product. That is just adding the action in the profiles/default/types/Attachment.xml generated by ArchGenXML 2.0. But there is a problem. That file has no special sections for custom code, so it will be overwritten every time we regenerate the product from our model.

My first attempt was to create another profile, called attachment, where I will put the modifications, and running the step from the Install.py. That is:

  • create a profiles/attachment/types/Attachment.xml and include there the action I need to add in a proper xml structure.

  • register the profile in the main configure.zcml (archgenxml will create a profiles.zcml for this purpose)
<!-- ##code-section profiles.zcml-top #fill in your manual code here -->
description="Customized extension profile for MyProduct."
<!-- ##/code-section profiles.zcml-top -->

  • put this code in Extensions/Install.py
from Products.CMFCore.utils import getToolByName

def afterInstall(self, reinstall, product):
portal_setup = getToolByName(self, 'portal_setup')

But when I installed the product, I got:

BadRequest: The id "import-all-profile-Products.MyProduct_attachment-20080426230436.log" is invalid - it is already in use.

I was importing the steps of a profile that QuickInstaller had already imported. But my product wasn't completely installed yet...

The problem is that QuickInstaller imported the attachment profile not because it imports every step, but because it imports the first registered profile in alphabetical order. At least, that's what I concluded, because, after moving profiles/attachment to profiles/extra, and renaming attachment for extra in the previous fragments of code, it worked.

The default profile wasn't imported. And looking at the installable products on QuickInstaller Control Panel page, the attachment product was listed, instead of the MyProduct profile that used to be there.

So, should I use only names that follows 'default' in lexicograpic order for additional profiles? I would expect that QuickInstaller only executed the default profile. Maybe I missed something... This was the first time I added a second profile.

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.