How to get python 2.4 or 2.5 for Ubuntu 10.4

Each new version of an OS carries its bad news. The last version of Ubuntu doesn’t embed neither Python 2.4 nor Python 2.5. Nevertheless until all your Plone sites will be migrated to Plone 4 you will need a Python 2.4 version somewhere.

Method 1: search for sources to get binary packages

There is a Python 2.4 « branch » in Ubuntu on launchpad :  “python2.4” source package in Lucid. If you now a little how Ubuntu packaging work you can easyly get Ubuntu package of Python 2.4 by following steps below:

  1. Click on Show builds
  2. Choose Successfully built then apply the filter
  3. Then choose your the archive for your architecture (often i386 or amd64)

There you have python2.4 and python2.4-dev packages.

Method 2: Use packages search tool

Launchap also have a package search tool for all Ubuntu releases. This one doesn’t limit itself to packages disbributed in official Ubuntu repositories and it also give results for all packages build using the Launchad system.

In our case we can use:

Method 3: Get sources and create your own packages

From the source page of the first method (“python2.4” source package in Lucid) you can get sources and the official patch for Ubuntu. For exemple you can click on 2.4.6-1ubuntu5 to get this version.

On the next page you can download a python source archive and the associated patch :

Once these to files downloaded you will do following steps:

  1. Unarchive python sources: tar zxf python2.4_2.4.6.orig.tar.gz
  2. Uncompress the patch:  gunzip python2.4_2.4.6-1ubuntu5.diff.gz
  3. Apply the patch:  patch -p0 < python2.4_2.4.6-1ubuntu5.diff
  4. Go in the folder created: cd python2.4-2.4.6
  5. Try to build the package: dpkg-buildpackage
  6. Install libreadline5-dev or fix the debian/control file by replacing libreadline5-dev by libreadline-dev (I’m nasty)
  7. Install all dependencies required (why do you need emacs and bluetooth to compile python ?): apt-get install libncursesw5-dev  tk8.5-dev libdb4.6-dev libgdbm-dev blt-dev  libbluetooth-dev emacs23 debiandoc-sgml
  8. Try again to build the package: dpkg-buildpackage

I don’t know how long these packages will be available.


Mise au point sur la licence GPL et les services web

J’avais déjà fait un billet à ce sujet il y a quelques temps. Lors des Journées du Logiciel Libre j’ai eu l’occasion de discuter avec Alix Cazenave après sa conférence sur la loi Hadopi et le contrôle du net.

Voici les points que j’ai pu éclaircir :

  • la licence GPL concerne le code d’un logiciel
  • les services web ne sont pas couverts par la licence GPL
  • c’est la licence Affero GPL qui couvre les services réseau

Lorsqu’une entité mets en œuvre des services réseau basés sur du code GPL elle n’a aucun devoir de partage du code source de son application avec ses utilisateurs ou avec la communauté. Cette entité peut vendre les services réseau sans contreparti mais si elle veut vendre l’installation de son application il lui faudra partager les sources.

Si le code d’un logiciel est protégé par la licence Affero GPL alors n’importe quel utilisateur interagissant avec service réseau basé sur son code peu demander le code source complet du service réseau.

Dans le cas de Plone 2 et de Plone 3 ils ne relèvent que de la seule licence GPL. Plone 4 utilisera sûrement une licence BSD lors de sa sortie.

Which Plone project is your site based on?

Far from the versions war I’m trying to understand how many people don’t use Plone « main » and choose to use project based on it. This evaluation is getting more difficult that I’m not able to recense how many projects are based on Plone. We can found some of them in the feed and some others in pypi or in the products column.

I can easily cite :

But they would be much more.

The Plone Conference in Budapest will be a good place for these projects to gain visibility and to project leaders to talk with the evangelist team to gain a page on to show what are their roadmap. It is difficult to find an announce done in a feed or in mailing lists few months ago. It easier to find a page one the official site if a projects column is created in it. The Plone foundation doesn’t have to fund them, but can help their communication.

Why Plone architecture must change

I proposed a conference on this theme but it was not keeped for PloneConf2008. Here comes a short work on its, and I will hope it create a debate.

Plone is born with a goal: make Zope 2 and CMF simple. Those two are frameworks and allowed you to create a website… a so rustic website. With Plone you only needed to fill one form to have a cool site ready to use and to custom. At that time Plone was CMFPlone, an other implementation of CMFDefault.

Some new products were inserted in the bundle like GroupUserFolder in a such way that they cannot be separated from the base implementation of Plone.

The second step was the Archetypes framework: a new manner to manage object’s attributes as schema. With all informations stored in the schema,  mutators and accessors are generated on boot time and forms are generated from schema’s widgets. It was cool, but not perfect. Then the product ATcontentTypes was created to be the glue to upgrade CMFPlone as Plone: all base types from CMF are now overloaded by a type from ATContentTypes.

So came Zope 3 and Five. Five is the glue that allows Zope 2 developpers to use component architecture from Zope 3 in their code. Plone 2.5 and Plone 3 are using Five.

Another product came from Zope ‘renegades’: PluggableAuthService and its implementation in Plone: PlonePAS. It should replace the old GroupUserFolder but users and groups management templates were never refactored.

Plone 3 introduces new component in to our greatest happiness.

What part of Plone needs CMF?
Why Plone needs to know about Archetypes storage and CMFEditions strategy?
Why GroupUserFolder is always in the bundle as PlonePAS fit is API?

Now Plone is like the linux kernel: a big monolithic Plone with a lot of modules that create a base Plone 3 site. And so much glue! GroupUserFolder is always here because nobody knows and wants to work on the portal_group replacement.
If you are following Plone4Artist or PloneGov you can see that a part of these projects needs to overload Plone base configuration.

CPS 3, an other CMF-based CMS, was conceived with the thinking that components need to be independant to be reusable and maintainable:

  • CPSSchema depends only on Zope
  • CPSCore depends only on Zope
  • CPSDocument depends only on CPSSchema
  • CPSDefault depends on CPSCore and CPSSchema, and implements the CPS site.

After 2 years it was divided into platforms:

  • CPS Legacy
  • CPS Courier
  • CPS Groupware

In front of that Plone propose at single product with a lot of glue that depends on others products or components that use their own glue and so on…
There’s no plone.core and plone.default and we cannot create a plone.artistsite or a plone.govsite.
Do you think that everyone need an openid or an ldap integration in Plone ?

Plone 4 must be a reimplementation, not only a new glue with new concepts. I don’t want any new functionality in Plone 4, I want modularity and scalability.

Write your first Zope 2 product

‘The topic is to make understandable what is a Zope product in the python world.

What is a python module?

A Python module is a directory on your filesystem that contain at least a file called This file can be empty, but the name is fixed.

How Python can find such a directory?
The standard path where python search for modules is in the directory site-packages of your Python installation. By default you find it in C:Python2.4Libpython under windows or in /usr/lib/python2.4 un Unix-like systems (Linux, Mac OS, BSD, etc). You can extend this search by adding the PYTHONPATH variable in your environment.

@set PYTHONPATH=C:Zope2.9libpython


export PYTHONPATH=/usr/lib/python2.4:/opt/Zope2.9/lib/python

In a Python module every sub-directory that contains a file named is considered as a sub-module.
On these points Zope doesn’t differ from Python

What is a Zope Products?

You can divide your Zope installation in three parts: the Zope framework, the Zope Server and the ZODB. A Zope product is an application for the Zope Framework. You can use it alone, without the Zope Server or the ZODB. Actually it’s the failure of Zope to not have any public software based on the Zope Framework without the Zope Server.
Zope 2 is using a specific code to load products to register them as specific applications. You can read it in your Zope installation in the file OFS/

During the warm up the Zope Server is looking in the zope.conf file for the ‘products‘ keywork. Each time it is defined this keywork is following by a filesystem path where Zope Products are uncompressed.
This list of filesystem paths is initialized with the two paths:

  1. the lib/python/Products directory of your Zope installation
  2. the Products directory of the Zope instance

The first one contains base application you need to work like ZCatalog, PythonScripts (for ZMI). You should never add a product in there and read the documentation available in these products will help you to understand Zope Framework standards.
The second one is empty and is designed to store cusomer Zope Products.

Implement your first Zope 2 product

To create your product you create a Python module in the Products directory. You should Have something like this:


> MyProduct


To be recognized as a Zope Product the file must contain a function called initialize. This function is mainly declarative. You can read a full sample on the document  » Write a toolbox for a Plone 2.5 product « .
The main goal of the initialize function is to register your new classes into the good drawer in the Zope Application. In the sample we register a new tool object but you will do the same for content types, i18n domains, etc.

Write a toolbox for a Plone 2.5 product

For years we have had the need to embed a utility in Plone products that allows us to execute private methods in restricted Python mode. You need such a utility when using a ZMI Python script in a Page Template or in an Expression. The last one is a CMFCore class that is used in workflow transition condition or in Archetypes field condition for example. To be able to do this utility you need to create a ‘portal tool’ for your site with some public methods. Here I will show you how to build a very basic utility that I can use in the next blog entry.

What is a tool

In pure Zope 2/CMF, a tool is a unique item with a forced id that inherits from the SimpleItem class. An instance of a tool is always created on the root of your Plone site to be acquirable from everywhere in the site.

The CMFCore product provides a function that allows us to call a tool in an unique way: ‘getToolByName’.
In python modules, external methods, or python scripts, you can use following code:

from Products.CMFCore.utils import getToolByName
mytool = getToolByName(obj or context, 'tool id', default value is not found - None if not specified)

You can use it in Page Template too, using the ‘module’ call:

python:modules['Products.CMFCore.utils'].getToolByName(here, 'tool id')


What should a tool module contain

Now back to our implementation.

First you need to have a product to write in. To create a new product please read this howto , or download someone else’s to play with.
In your product create a file called ‘,’ if there will be only one tool in your product, or ‘’ if there will be more than one. As a best practice, I recommend to always choose the second way.

A simple tool code could be:

from OFS.SimpleItem import SimpleItemfrom Products.CMFCore.utils import UniqueObject

from AccessControl import ClassSecurityInfo

from Globals import InitializeClass

class ProductNameTool(SimpleItem, UniqueObject):

    """ Description of what your tool handle

    id = 'portal_productname'

    meta_type = 'Product Name Tool'

    manage_options = SimpleItem.manage_options
    security = ClassSecurityInfo()


Description of imported items:

  • SimpleItem is a class coming from Zope that ensures us that we have persistence and base views available in ZMI.
  • UniqueObject is a utility class coming from CMF that ensures us that nobody can rename an instance of it. It’s important for us because our tool must never be renamed by error.
  • ClassSecurityInfo is the class to use in every Zope persistent class in order to define the security of each method.
  • InitializeClass is the helper function that registers our class in the Zope context.

Description of class attributes:

  • The id must be unique on your site root and should start with ‘portal_’ or end with ‘_tool’. If your tool unherit from ZCatolog the id must be suffixed by ‘_catalog’.
  • All classes must have a meta type. You can just extend your class name with spaces.
  • manage_options contains the tabs displayed in the ZMI.
  • security defines the security in your class. All classes must contain such a declaration.

Initialize our tool at Zope startup

Now that Zope knows that you have a tool, what about Plone? Plone requires an extra step in order to register our tool (full details in: « Write your first Zope 2 product« .
We have to edit the file of our product and add following lines:

from Products.CMFCore.utils import ToolInit

from myfunctiontool import ProductNameTool

tools = (ProductNameTool,)

def initialize( context ):

    ToolInit( 'My Product Tools'
                , tools=tools
                , product_name='ProductName'
                , icon='tool.gif'
                ).initialize( context )

ToolInit is a class that handles tool registration for us. We can register all tools defined in our product in only one pass.
ProductNameTool is our tool class. Here we need to import all tool classes we want to register.

At last, we call the ToolInit constructor and run its ‘initialize‘ method to build our tool factories:

  • the first parameter is the name viewed in the Add dropdown menu in the ZMI
  • tools is a tuple of classes
  • product_name should contain the name of your product
  • icon is the name of the icon displayed in the ZMI (16×16) that must be on the root of your product

We can restart Zope then add the tool in the ZMI. After that, we can add methods to this tool. Most often these methods are public or protected by ‘View’ or ‘Manage portal’ permissions.

Future of Zope 2 tools

In Zope 3 tools are becoming utilities. In Plone 3 we are in limbo, with all old Zope 2 tools registered both as tools and as utilities. In the near future, however, only the utilities will be used. Use Tool for Plone 2.5 development only.

You can read more about utilities in Zope 3 and Plone 3 documentation.

Use google document to create the Plone Documentation

Today I was writing a little documentation about Plone 2.5 in reaction to a first draft on workflow management by groups. My goal is not to write a document alone that will replace this draft but to imply its author. A this point a tool was missing until I remember we can use Google Docs application to share our ideas and write this document together.

We have shortly try out Google Docs and our first impressions are positive: each other can write in the same document and modification are reported in live on the other screen. Add a GTalk window or an IRC session to discuss and you have a very powerful tool.

The next step is to finish this document and to copy/paste it in Plone Documentation. I hope to do this tomorrow night.

Hey Limi, when will we embed this in Plone ;^)

Write functionnal doctests with files in forms

A short bill about testing forms with files.

First you need to setup you environment using your site policy product then your
product to test. The function setUpDefaultMembersAndFolder create the
structure with access rights positioned. After that you can focus yourself on
Formula module test.

Test Formule creation process

For the test you need to create three Nomenclatures and two Validation Process.

First, some set-up:

    >>> from Products.Five import zcml
    >>> import Products
    >>> import iw
    >>> zcml.load_config('configure.zcml', package=iw.sitepolicy)
    >>> zcml.load_config('configure.zcml', package=Products.Formula)

    >>> from iw.sitepolicy.tests import utils
    >>> utils.setUpDefaultMembersAndFolder(self)

    >>> from Products.Five.testbrowser import Browser
    >>> browser = Browser()
    >>> browser.handleErrors = False

Let us log all exceptions, which is useful for debugging. Also, clear portlet
slots, to make the test browser less confused by things like the recent portlet
and the navtree.

    >>> self.portal.error_log._ignored_exceptions = ()
    >>> self.portal.left_slots = self.portal.right_slots = []

Import needed for testing file in forms

    >>> import cStringIO
    >>> portal_url = self.portal.absolute_url()

We will need of cStringIO to create fake files.

Now we will logon with an user and jump directly into working folder.

    >>>'%s/logout' % portal_url)
    >>> + '/login_form')
    >>> browser.getControl('Login Name').value = 'jdoe'
    >>> browser.getControl('Password').value = 'secret'
    >>> browser.getControl('Log in').click()
    >>> 'John Doe' in browser.contents
    >>> browser.url

Verify that an author keeps modification and creation rights

    >>> self.formula_folder.absolute_url()+'/edit' in browser.contents

    >>> self.formula_folder.absolute_url()+'/createObject?type_name=BaseFormula' in browser.contents

self.formula_folder is set in setUpDefaultMembersAndFolder.

Now we can fill the form and test the result. Document’s id is the filename
prefixed by ‘formula_‘ and document’s title is the first file line.

Create a new Formula for with special pink flavor

    >>> browser.getLink('Add Base Formula').click()
    >>> 'portal_factory' in browser.url
    >>> browser.getControl(name='special').value = u"pink flavor"
    >>> browser.getControl(name='formula_file').add_file(cStringIO.StringIO('Pink flavor\n   etc'), 'text/plain', "32048432.txt")
    >>> browser.getControl('Save').click()
    >>> 'Changes%20saved' in browser.url
    >>> browser.getLink('View').click()
    >>> '<h1>Pink flavor</h1>' in browser.contents
    >>> print browser.url

If you want to do the same with Plone 2.5 – Zope 2.9 – you will have an error
because the ‘add_file‘ is not implemented on FileControl. You need to use
following code:


Manage your internationalization with i18ndude

Yet another post to manage automatically you po files…

We are using i18ndude to manage translation for the Plone bundle and helpers script are in PloneTranslations/utils. If we can do this for the whole bundle you can do it too for your products.

Here come an example of a script that manage translations for a Plone 2.5 product:


## First we work on the 'plone' domain and then on the local domain to define below.


## Domain: 'plone'

## Search all translations in 'plone' domain and rebiuld a new catalog
i18ndude rebuild-pot --exclude build --pot "./i18n/i18ndude-plone.pot" \
                     --create plone "./"

## Filter out all msgids that are already translated in PloneTranslations
i18ndude filter "./i18n/i18ndude-plone.pot" \
                "${PLONEPRODUCTS}/PloneTranslations/i18n/plone.pot" > "./i18n/filtered-plone.pot"

## Merge generated file with manual maintained pot file then with the current catalog
i18ndude merge --pot "./i18n/${POPREFIX}-plone.pot" \
               --merge "./i18n/filtered-plone.pot" \
               --merge2 "./i18n/manual-plone.pot"

## Cleaning
rm "./i18n/filtered-plone.pot" "./i18n/i18ndude-plone.pot"

## Refresh po files for 'plone' domain
i18ndude sync --pot "./i18n/${POPREFIX}-plone.pot" \
                    "./i18n/${POPREFIX}-plone-fr.po" "./i18n/${POPREFIX}-plone-en.po"


## Search all translations in ${LOCALDOMAIN} domain and rebiuld a new catalog
i18ndude rebuild-pot --exclude build --pot "./i18n/i18ndude.pot" \
                     --create ${LOCALDOMAIN} "./"
## generated.pot is given by ArchGenXML during product generation
i18ndude merge --pot "./i18n/${POPREFIX}.pot" \
               --merge "./i18n/i18ndude.pot" \
               --merge2 "./i18n/generated.pot"
## Cleaning
rm "./i18n/i18ndude.pot"

## Refresh po files for LOCALDOMAIN
i18ndude sync --pot "./i18n/${POPREFIX}.pot" \
                    "./i18n/${POPREFIX}-fr.po" "./i18n/${POPREFIX}-en.po"

## Check for missing translations in all page templates
echo "#########################################################"
echo "##"
echo "## untranslated messages summary report"
i18ndude find-untranslated -s `find skins -name "*.*p?"`

echo "To display the full report use:"
echo "i18ndude find-untranslated \`find skins -name \"*.*p?\"\`"

exit 0

You can repeat this for every domain you are using in your products.
You still have to manage a manual-domain.pot by hand to be able to i18n selectboxes or Archetype name for a content type for example, but your work is really easier to maintain you translations.

Thanks to Hanno for maintaining this tool.