Monday, May 16, 2011

Managing virtualenvs with mkvirtualenv

Read this, thanks Doug!

Thursday, May 5, 2011

Commands to replace xdv namespaces with diazo

The plone.app.theming page is clear enough about migrating products from collective.xdv to plone.app.theming. Here are some commands to make the necessary namespace substitutions in xml files:


perl -p -i -e 's/xmlns=\"http:\/\/namespaces.plone.org\/xdv\"/xmlns=\"http:\/\/namespaces.plone.org\/diazo\"/' *.xml
perl -p -i -e 's/xmlns:css=\"http:\/\/namespaces.plone.org\/xdv\+css\"/xmlns:css=\"http:\/\/namespaces.plone.org\/diazo\/css\"/' *.xml

Wednesday, May 4, 2011

Creating Plone content when installing / upgrading a product

[update 2012-05-23: link to GS import/upgrade at http://collective-docs.readthedocs.org/en/latest/components/genericsetup.html#custom-installer-code-setuphandlers-py]
[update 2013-03-23: underscore added to function call]

Following up on my previous post, i've extended the method to create objects a bit.

Short reminder: this method is intended for use when Generic Setup (profiles/default/structure) can't create content in the way you want, for example when you have custom content types, or want to change "exclude_from_navigation" settings or workflow state. This article assumes you will call your setuphandler.py's methods from a Generic Setup import or upgrade step.

## setuphandlers.py

from Products.CMFPlone.utils import _createObjectByType
from Products.CMFCore.utils import getToolByName
from Products.CMFCore.WorkflowCore import WorkflowException

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.

    if context.readDataFile('My.Product_various.txt') is None:
        return

    # Add additional setup code here

def _my_structure():
    return [
        {   'id': 'new-folder', 
            'title': 'New Folder',
            'description': 'Folder for authenticated users',
            'type': 'Folder',
            'workflow_transition': 'retract',
            'exclude_from_nav': True,
            },
        ]

def _createObjects(parent, children):
    """This will create new objects, or modify existing ones if id's and type
    match.

    This takes two arguments: the parent to create the content in, and the
    children to create.

    Children is a list of dictionaries defined as follows:

    new_objects = [
        {   'id': 'some-id', 
            'title': 'Some Title',
            'description': 'Some Description',
            'type': 'Folder',
            'layout': 'folder_contents',
            'workflow_transition': 'retract',
            'exclude_from_nav': True,
            'children': profile_children,
            },
        ]
    
    * layout:               optional, it sets a different default layout
    * workflow_transition:  optional, it tries to start that state transition
        after the object is created. (You cannot directly set the workflow to 
        any state, but you must push it through legal state transitions.)
    * exclude_from_nav:     optional, excludes item from navigation
    * children:             optional, is a list of dictionaries (like this one)

    """

    parent.plone_log("Creating %s in %s" % (children, parent))

    workflowTool = getToolByName(parent, "portal_workflow")

    existing = parent.objectIds()
    for new_object in children:
        if new_object['id'] in existing:
            parent.plone_log("%s exists, skipping" % new_object['id'])
        else:
            _createObjectByType(new_object['type'], parent, \
                id=new_object['id'], title=new_object['title'], \
                description=new_object['description'])
        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'])
        else:
            if obj.Type() != new_object['type']:
                parent.plone_log("types don't match!")
            else:   
                if new_object.has_key('layout'): 
                    obj.setLayout(new_object['layout'])
                if new_object.has_key('workflow_transition'): 
                    try:
                        workflowTool.doActionFor(obj, 
                            new_object['workflow_transition'])
                    except WorkflowException:
                        parent.plone_log(
                            "WARNING: couldn't do workflow transition")
                if new_object.has_key('exclude_from_nav'):
                    obj.setExcludeFromNav(new_object['exclude_from_nav'])
                obj.reindexObject()
                children = new_object.get('children',[])
                if len(children) > 0:
                    _createObjects(obj, children)

def createContent(context):
    portal = getToolByName(context, 'portal_url').getPortalObject()
    _createObjects(portal, _my_structure())