I like programming in Python. I like learning about it, I like using it, and so I wish to share it.

Friday, June 19, 2009

Export scripts run from Plone/Zope application server

To extract data from a plone instance, the easiest way is to use the plone_catalog tool. Using this tool we could search for the data (according to type, path and other criteria of our choosing) and iterate over every row that it finds.

To do that first we have to initialize the tool by importing the getToolByName function and assigning the portal_catalog tool to a variable.

from Products.CMFCore.utils import getToolByName

catalog = getToolByName(obj,'portal_catalog')

And then we can use that catalog to collect the data which we want. For example to collect all the pages that is in the entire site we would do something like so:

pages = catalog.searchResults(portal_type='Page')

And then we would iterate over the results:

for page in pages:
print page.title

Or maybe we want to limit our search to only pages inside a certain folder. Then we would have to specify the path to that folder as criteria to our search like so:

pages = catalog.searchResults(path='/inigo/projects',portal_type='Page')

The query above would return only the pages under the folder projects in the inigo folder.

If the id of the item that is wanted is already known, then we can also specify that as a criteria:

pages = catalog.searchResults(id='project_members',portal_type='Page')

Every criteria can have additional value to compare with that is combined with 'or' or 'and'. For example if you wanted to look for all the pages and folders under the '/inigo/projects' folder, you would it like so:

pages = catalog.searchResults(path='/inigo/projects',portal_type={'query':['Page','Folder'],'operator':'or'})

Note that the results we get from the portal_catalog tools are in the form of brains which is basically the indexed data of the object we have searched for. This is to prevent the search from 'waking up' the object which would be quite costly in terms of processing. But if we require some data to be read from the results which is not indexed, then we need to refer to the object itself like so:

pages = catalog.searchResults(portal_type='Page')
for page in pages:
page_object = page.getObject()

Example Script

#!/usr/bin/env python
from Products.CMFCore.utils import getToolByName
from Testing import makerequest

def runit(app):
app=makerequest.makerequest(app)
catalog = getToolByName(app.inigo, 'portal_catalog')
pages = catalog.unrestrictedSearchResults(portal_type="Document")
print '%d pages found' % len(pages)
for page in pages:
pageobj=page._unrestrictedGetObject()
print 'Title:',pageobj.title
print 'Description:',pageobj.description
print 'Content:',pageobj.getText()

runit(app)

The script above will display the pages(documents) available on the inigo site in a plone instance. If the script above is saved in the root plone directory as showpages.py, then it can be executed by running:

bin/instance run showpages.py

The unrestrictedSearchResults function is used so that even pages that are private or not published are listed. But to be able to get those pages the function _unrestrictedGetObject have to be used to get the object.

Wednesday, June 17, 2009

Running python scripts on a plone instance

Recently I had to do some heavy importing from MySQL into ZODB (aka Plone). It turns out to be easier than I expected. You can virtually run any python script with a zope instance. Just do a:


bin/instance run <name of script>

So any script you want would be able to access the global variable 'app' to do things to that instance. For example if you had a Plone site called 'taskmanager' which has the id 'taskmanager', then you'd access it in your script using:

app.taskmanager

Once you've got the object from the instance you can start adding stuff to it. I used the _createObjectByType function to avoid any kind of permission checking about creating the object. So first include it into your script and then go crazy.. :P:

from Products.CMFPlone.utils import _createObjectByType
newfolder = _createObjectByType('Folder',app.taskmanager,id='Task',title='Task')

You can guess what's the variable of the function. First the type of object you want to create. Then the parent of that object. Then the other stuff like id and title and all the other attribute of that object which you might want to give.