Creating an uninstall profile
So far you have learned how to add configuration to a Plone site with GenericSetup... But what if you want to remove it again? Typically any
Plone Product author wants to offer uninstall of his project.
Fortunately, GenericSetup allows you to pinpoint and remove selected elements. From a high level you do this:
- Create an additional GenericSetup profile with the name "uninstall" - many times you can just copy the "default" profile
- Sprinkle remove="True" attributes around the XML files
- Wire the uninstall profile up with the Install.py of your product if you like.
How to create an uninstall profile
Consider a profile that configures a custom portal type, e.g. the following lines in types.xml and the corresponding MyType.xml<?xml version="1.0"?>Now, copy the types.xml over to a new profile directory called uninstall and alter it a bit:
<object name="portal_types" meta_type="Plone Types Tool">
<object name="MyType"
meta_type="Factory-based Type Information with dynamic views"/>
</object>
<?xml version="1.0"?>That's it. Run the new profile and it will remove the MyType configuration.
<object name="portal_types" meta_type="Plone Types Tool">
<object name="MyType"
meta_type="Factory-based Type Information with dynamic views" remove="True" />
</object>
If you like, you can clean it up a bit:
<?xml version="1.0"?>
<object name="portal_types">
<object name="MyType" remove="True"/>
</object>
What works and what doesn't yet
The magic "remove" attribute has been implemented for many of the GenericSetup handlers, but not all of them.Remove works for these handlers:
- Skins
- Types
- Workflows
- Catalog indexes
It does not work for these -- yet:
- Actions
- Properties
- Configlets
I haven't tested with the rest of the handlers.
Wiring up the Quick Installer
You can wire up the uninstall profile with Quick Installer the same way as you wire up a normal install profile, e.g.from Products.CMFCore.utils import getToolByNameUntil all the GenericSetup handlers support the "remove" attribute, you might need a hybrid uninstall method that runs an uninstall profile and uninstalls the remaining elements using old style scripting afterwards.
def uninstall(portal):
setup_tool = getToolByName(portal, 'portal_setup')
setup_tool.setImportContext('profile-MYPRODUCT:uninstall')
setup_tool.runAllImportSteps()
setup_tool.setImportContext('profile-CMFPlone:plone')
return "Ran all uninstall steps."
Removing skins with an uninstall profile
In the skins.xml file of your uninstall profile make sure you change default_skin to a skin that exists ("Plone Default" is a safe bet.) In your install profile, skins.xml will have something like this:
<object
name="portal_skins"
allow_any="False"
cookie_persistence="False"
default_skin="My Skin"> ... </object>
In your uninstall profile change default_skin="My Skin" to default_skin="Plone Default".
Otherwise when your skin is being removed the portal_skins tool will still have "My Skin" set as the default (despite portal_skins' properties in the ZMI saying otherwise) and your site will break because the skin doesn't actually exist.
Further, as far as I can tell if you have a skins.xml in your uninstall profile you can't programmatically set a different default_skin in the uninstall function in Install.py. I tried this because I wanted to ensure I set a skin that existed rather than blindly assuming "Plone Default" was there. However, it seems the skins.xml uninstall profile is imported after the uninstall function runs. Setting default_skin in my uninstall function wasn't working in conjunction with having a skins.xml in my uninstall profile where I hadn't updated the default_skins attribute.