Friday, February 27, 2009

Linking to translated content items from zope page templates

This document is now a how-to on

I had a situation where a custom footer links to a "Terms of Service" page. This page was a Page (Document) object with LinguaPlone enabled.

What happens is this:

  1. Set your browser's language to Dutch.

  2. Log in. Note that all texts are in Dutch.

  3. Click the link "Voorwaarden" (or whatever, it links to 'terms-of-service'

  4. You get the item in English instead of Dutch.

This is not desirable. We should get the translation, which has been created.

So how to link to it so it gets the right language? Two things:

  • Add a script getContentTranslation:
    ## Script (Python) "getContentTranslation
    ##bind context=context
    ##title=Get the preferred translation, if it's there.

    served_languages = context.portal_languages.listSupportedLanguages()
    boundLanguages = context.portal_languages.getLanguageBindings()
    prefLang = boundLanguages[0]
    return (hasattr(context, 'getTranslation') and context.getTranslation(prefLang)) or context;

  • In you TAL, do this:
            <span id=""
    tal:define="link_object python: context.portal_url.get(content_id, None);"
    tal:condition="python: link_object is not None"
    tal:attributes="id content_id">
    <a href=""
    translated_object link_object/getContentTranslation"
    tal:attributes="href translated_object/absolute_url">

This also takes care of inserting the translated title as the link title, and it checks for existence of the content object (whose id is defined in content_id).

Monday, February 23, 2009

My final word on RewriteRules

See this excellent page: The RewriteRuleWitch

Thursday, February 12, 2009

Convert number to scientific notation

Here's a snip to convert a number to scientific notation in Python:

def convertToScientific(nr):
coefficient = float(nr)
exponent = 0
while abs(coefficient) >= 10:
exponent += 1
coefficient = coefficient / 10
while abs(coefficient) < 1:
exponent -= 1
coefficient = coefficient * 10
return (coefficient, exponent)

Wednesday, February 4, 2009

Creating portal content in

Updated; see

To add portal content, you can use GenericSetup's profiles/default/structure mechanism. However, this does not allow to set the default view of folders. To gain more flexibility, i added some methods to

from Products.CMFPlone.utils import _createObjectByType

def deletePloneFolders(p):
"""Delete the standard Plone stuff that we don't need
# Delete standard Plone stuff..
existing = p.objectIds()
itemsToDelete = ['Members', 'news', 'events']
for item in itemsToDelete:
if item in existing:

def createFolderStructure(portal):
"""Define which objects we want to create in the site.
profile_children = [
{ 'id': 'meters',
'title': 'My Meters',
'description': '',
'type': 'Folder',
'layout': 'metertypeform',
top_folders = [
{ 'id': 'profile',
'title': 'My Profile',
'description': '',
'type': 'Folder',
'layout': 'wm-profile',
'children': profile_children,
{ 'id': 'meterreadings',
'title': 'My Meter Readings',
'description': '',
'type': 'Folder',
'layout': 'meterreadings',
{ 'id': 'analysis',
'title': 'My Consumption',
'description': '',
'type': 'Folder',
'layout': 'analysis',
createObjects(parent=portal, children=top_folders)

def createObjects(parent, children):
"""This will create new objects, or modify existing ones if id's and type
parent.plone_log("Creating %s in %s" % (children, parent))
existing = parent.objectIds()
parent.plone_log("Existing ids: %s" % existing)
for new_object in children:
if new_object['id'] in existing:
parent.plone_log("%s exists, skipping" % new_object['id'])
_createObjectByType(new_object['type'], parent, \
id=new_object['id'], title=new_object['title'], \
parent.plone_log("Now to modify the new_object...")
obj = parent.get(new_object['id'], None)
if obj is None:
parent.plone_log("can't get new_object %s to modify it!" % new_object['id'])
if obj.Type() != new_object['type']:
parent.plone_log("types don't match!")
children = new_object.get('children',[])
if len(children) > 0:
createObjects(obj, children)

def setupVarious(context):

# Ordinarily, GenericSetup handlers check for the existence of XML files.
# Here, we are not parsing an XML file, but we use this text file as a
# flag to check that we actually meant for this import step to be run.
# The file is found in profiles/default.

portal = context.getSite()

if context.readDataFile('MyProduct.theme_various.txt') is None:

# Add additional setup code here