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.


Add a new role in the sharing tab for Plone 3

If you want to display a new role in the sharing tabe of Plone 3 it requires a
good knowledge of ZCA. First you need to create another specific role then an
utility. These complexity allows you to control which roles are displayed
using workflows.

Lets take the example of the well known Manager role.

Create the delegating role

First you have to define a new role. The auto defined convention said that it
should be named as Sharing page: Delegate XXX role. As often you need to
register this new role both in Python and ZCML:

# -*- coding: utf-8 -*-
from Products.CMFCore.permissions import setDefaultRoles
from AccessControl import ModuleSecurityInfo

security = ModuleSecurityInfo("my.egg.permissions")

DelegateManagerRole = "Sharing page: Delegate Manager role"
setDefaultRoles(DelegateManagerRole, ('Manager',))


<configure xmlns=""

    <!-- Our custom permissions -->
        title="Sharing page: Delegate Manager role"


Create your Role utility

Now you have a delagating role to control how to share your role you need to
create an utility that implements the ISharingPageRole interface. This
interface requires two attributes:

  • title: a translated title
  • required_permission: the permission defined just before

# -*- coding: utf-8 -*-
from zope.interface import implements
from import ISharingPageRole

from my.egg import permissions
from my.egg import MyEggMessageFactory as _

class ManagerRole(object):

    title = _(u"title_can_manage", default=u"Can manage")
    required_permission = permissions.DelegateManagerRole

At last we have to register this utility and the permissions.zcml defined


<include file="permissions.zcml" />

  <!-- Local roles managed in the @@sharing view -->

By default the sharing tab hide Manager, Owner and Member role
because they can create troubles when they are used in a wrong way. If you
think to show them be sure that persons that will use it are informed how they
can put their site upside down.

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.

Manage monkey patches and performance improvement in Plone 3

Few days ago Jean-Michel François proposed a useful patch for PlonePAS that can be applied for all Plone release until 3.2. Plone 3.3 will embed this patch.

How can we add this patch in a traceable way for an not so old Plone 3.1 or 3.2 ?

First, we can use the new release  of Products.PlonePAS that should be compatible with our Plone installation. The second option is to add a monkey patch in the policy product of our site. One more monkey patch…

Some projects have so many monkey patches that it is difficult to know where is the code that run your site. Martin Aspeli did a tool to handle monkey patches in an elegant way for Zope 2 and Zope 3: collective.monkeypatcher. It allows you to plug  your monkey patches with a simple ZCML directive. Later Gilles Lenfant added a control panel for Zope 2 to be able to have a visual way to follow patches with collective.monkeypatcherpanel.

How does it works?

In your buildout.cfg add :

eggs +=
zcml +=

To create patches add a ‘‘ file in your egg (if you have more than 2 or 3 patches you should create a directory). Our performance patch looks like this:

import copy

def enumerateUsers( self
                  , id=None
                  , login=None
                  , exact_match=False
                  , **kw

    """ See IUserEnumerationPlugin.
    plugin_id = self.getId()

    if id is not None:
    if login is not None:

    if not kw and id:
        data = self._storage.get(id, None)
        if data is None:
            user_info = []
            user_info=[ { 'id' : self.prefix + id,
                     'login' : id,
                     'title' : data.get('fullname', id),
                     'description' : data.get('fullname', id),
                     'email' : data.get('email', ''),
                     'pluginid' : plugin_id } ]
        users=[ (user,data) for (user,data) in self._storage.items()
                    if self.testMemberData(data, criteria, exact_match)]

         user_info=[ { 'id' : self.prefix + user_id,
                     'login' : user_id,
                     'title' : data.get('fullname', user_id),
                     'description' : data.get('fullname', user_id),
                     'email' : data.get('email', ''),
                     'pluginid' : plugin_id } for (user_id, data) in users ]

        return tuple(user_info)

In the configure.zcml of your policy product add an include:

<include file="patches.zcml" />

The file patches.zcml will contain following code:


    <include package="collective.monkeypatcher" file="meta.zcml" />



Run your buildout, start your site and the patch is applied. You can go in the Zope Control Panel to see how many patches are already compatible with this tool.

Heads up: new plone.recipe.pound release

Since few weeks plone.recipe.pound 0.5.4 was broken with an output like this:

An internal error occured due to a bug in either zc.buildout or in a recipe being used:
Traceback (most recent call last):
File "/tmp/tmpVcq-b1/zc.buildout-1.2.1-py2.4.egg/zc/buildout/", line 1509, in main
File "/tmp/tmpVcq-b1/zc.buildout-1.2.1-py2.4.egg/zc/buildout/", line 473, in install
File "/tmp/tmpVcq-b1/zc.buildout-1.2.1-py2.4.egg/zc/buildout/", line 1091, in _call
File "/home/encolpe/.virtualenvs/internal/preprod/plone.recipe.pound/plone/recipe/pound/", line 74, in install
installed = CMMIRecipe.install(self)
File "/home/encolpe/.virtualenvs/internal/preprod/downloads/dist/zc.recipe.cmmi-1.2.0/src/zc/recipe/cmmi/", line 155, in install
system("make install")
File "/home/encolpe/.virtualenvs/internal/preprod/downloads/dist/zc.recipe.cmmi-1.2.0/src/zc/recipe/cmmi/", line 27, in system
raise SystemError("Failed", c)
SystemError: ('Failed', 'make install')

This recipe directly unherit from zc.recipe.cmmi and act as a wrapper around the CMMIRecipe.install method. Most arguments used are transmitted to the recipe are self attributes or keys in self.options. The version 1.2.0 of this recipe publish the May, 18 moved the way that extra arguments are transmitted:

  • zc.recipe.cmmi 1.1.x:  self.options[‘extra_options’]
  • zc.recipe.cmmi 1.2.0: self.extra_options (an empty string by default)

There’s no regression tests, neither an entry in the release history. Youenn did a new release of plone.recipe.pound to fix this new behavior.


  • if you are using zc.recipe.cmmi 1.1.x you can still use plone.recipe.pound 0.5.4
  • if you are using zc.recipe.cmmi 1.2.0 or above use plone.recipe.pound 0.5.5
  • other recipes using zc.recipe.cmmi may be broken

‘Practical Plone 3’ review

I finished to read the long waited (read updates here 🙂) ‘Practical Plone 3’ book. It reprensents a big amount of very good work.

If you want to begin in Plone or if you want a webmaster guide for your brand new Plone site the two first parts are designed for you. Each step of your needs are describe in a very good teaching way.

Parts are growing in difficulties. The first will show you how to install and what in installed in a default Plone site. The second part will learn you how to become the owner of your site by creating content and configuring it. The third part is for those who wanted to learn how to use addon products for Plone or want to customize Plone site: zc.buildout, PloneFormGen and ArchGenXML are presented here. The last part will show you some general needs around Plone: put Plone in production behind Apache or IIS with speed optimizations, and LDAP/Active Directory integration.

Even if the target of this book is beginners it was a very interresting reading for an old developper like me. If you don’t use it for yourself, you will use it to give answers to your end-users. I can only recommand everyone to buy it. Nice work guys.

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.