Document Actions

Plone API

by Admin last modified 2006-04-27 07:57

Applies to Plone versions 2.0 and 2.1. This is an introduction to Plone's api usage, developed for ifPeople's Plone/Zope training in Argentina by Emanuel Sartor (emanuel at menttes.com) - menttes: http://www.menttes.com, and translated to English by Natalia B. Bidart (nbidart at except dot com dot ar) - except: http://except.com.ar. The tutorial is also available in Spanish - http://www.ifpeople.net/fairsource/courses/material/apiPlone .

This is an introduction to plone's api usage, by Emanuel Sartor (emanuel at menttes.com) - menttes: menttes.com, translated by Natalia B. Bidart (nbidart at except dot com dot ar) - except: excpet.com.ar.

Introduction

This document describes how Plone's objects can be manipulated by using Plone/Zope's API.

Very often, we'd like to manipulate Plone's objects by using scripts (Python). We are going to learn how to use Plone/Zope's API in order to be able to:

  • Manipulate content objects.
  • Use Plone's catalog.
  • Manipulate users and groups.

Manipulate content objects

Script's examples to create, delete and modify content objects.

We call content objects to those objects that can be added through Plone's interface in order to keep the portal's content: documents, images, links, folders, news and files.

The content objects can be manipulated by means of scripts written in Python. In other words, content objects can be created, modified and deleted; its state can be modified, and the information each object holds can be accessed.

Example 1: how to access a content object from a Python script.

First of all, let's create a content object through Plone's interface. Then, we're going to access it from a script according to this steps:

  • Add a document (Page) with id my_document. This step is sightly different if we're working under Plone 2.1.1 because the page's adding form only requests for the page's title, in this case we write My Document (and the id gets automatically generated).
  • From ZMI, navigate trough your Plone Site's folder and go to portal_skins/custom. There, add a script (Script (Python)) with the following content:

      from Products.CMFCore.utils import getToolByName

    urltool = getToolByName(context, "portal_url")
    portal = urltool.getPortalObject()
    document = getattr(portal, "my_document")
    print document.Title()
    print document.CookedBody()
    return printed

In the above example, we can see the use of getToolByName and getattr functions, and getPortalObject, Title and CookedBody methods. Let's dig deeper:

  • getToolByName(obj, name, default=[]) It's imported from a CMF module. It returns the tool given by name associated with obj.
  • getattr(object, name[, default]) Is a zope's function and it returns the attribute name's value from object. name has to be a string. If object has no attribute named name, an AttributeError exception is raised, except that the third (optional) argument is set.
  • hasattr(object, name) Is a zope's function and it returns True if the string name is the name of any attribute of object, False otherwise.
  • getPortalObject(self) This is a class method from Products.CMFCore.URLTool.URLTool. It returns the Portal object.

Example 2: how to modify a document's content from a Python script.

This is done by modifying the script shown in example 1: we add a new sentence before the print block:

document.edit(text_format="html",
text="<div><b>This is a new text...</b></div>")

edit method can be used according to the following:

  • edit(self, text_format, text, file="", safety_belt="") Method that belongs to CMFDefaults.Document.Document class. text_format can be any of html, structured-text, or plain. text is the document's body.

Example 3: how to copy and paste content objects from a Python script.

  • Create a folder with id my_folder. Remember that this process is a little different if working with Plone 2.1.1 (you only have to provie the new folder's name).
  • Below, a script tha copies the recently created document my_document into the new folder 'my_folder':

      from Products.CMFCore.utils import getToolByName

    urltool = getToolByName(context, "portal_url")
    portal = urltool.getPortalObject()
    cb_copy_data = portal.manage_copyObjects(["my_document"])
    folder = getattr(portal, "my_folder")
    folder.manage_pasteObjects(cb_copy_data)

Here, we can distinguish the use of two (three) important methods:

  • manage_copyObjects(self, ids=None, REQUEST=None, RESPONSE=None) Method from OFS.CopySupport.CopyContainer class. It places a reference to the objects identified by ids list inside the pasteboard.
  • manage_cutObjects(self, ids=None, REQUEST=None) Same as above.
  • manage_pasteObjects(self, cb_copy_data=None, REQUEST=None) Method from OFS.CopySupport.CopyContainer class. It pastes the object referenced by cb_copy_data, which has to be the result of either manage_copyObjects or manage_cutObjects.

Example 4: how to delete a content object from a Python script.

The following script deletes the document created on example 1:

from Products.CMFCore.utils import getToolByName

urltool = getToolByName(context, "portal_url")
portal = urltool.getPortalObject()
portal.manage_delObjects(["my_document"])

manage_delObjects is the key method on this example:

  • manage_delObjects(self, ids=[], REQUEST=None) Method from CMFPlone.PloneFolder.BasePloneFolder class. It deletes all elements which id belongs to ids list.

Example 5: how to create a document, a folder and an event from a Python script.

Consider the following script:

from Products.CMFCore.utils import getToolByName
urltool = getToolByName(context, "portal_url")
catalogtool = getToolByName(context, "portal_catalog")
portal = urltool.getPortalObject()
# create a document
doc = portal.invokeFactory("Document", "test_doc")
document = getattr(portal, "test_doc")
#document.setTitle("Test document")
#document.setDescription("This is the description of a test document")
document.editMetadata(title="Test document",
description="This is the description of a test document",
subject="")
document.edit(text_format="html",
text="<b>This is a test document!<b>")
# create a folder
fld = portal.invokeFactory("Folder", "test_folder")
folder = getattr(portal, "test_folder")
folder.setTitle("My Folder")
folder.setDescription("This is the description of a test folder")
catalogtool.refreshCatalog()
# create an event
evt = folder.invokeFactory("Event", id="event")
event = getattr(folder, "event")
event.edit(title = "Foo",
start_date="2003-09-18",
end_date="2003-09-19",
location="home",
description="This is the description of a test event")
event.editMetadata(subject="Appointment")

Here, a few explanations about the methods used in the above example:

  • editMetadata(self, obj, allowDiscussion=None, title=None, subject=None, description=None, contributors=None, effective_date=None, expiration_date=None, format=None, language=None, rights=None, **kwargs) Method defined in CMFPlone.PloneTool.PloneTool class.
  • invokeFactory(self, type_name, id, RESPONSE=None, args, *kw) type_name is the name of the content type target for creation. Please note that this id has to be the same as the one shown in the tool portal_types (but it might be different from the type name shown in Plone's user interface). id is the identifier (short name) of the new object: this name is used to later build the url. This method accepts some others parameters that are passed to the object's constructor method. One of the most common is title.
  • refreshCatalog(self, clear=0) This methods reindexes all objects found inside self.

Example 6: how to change the state of a private document from a Python script.

Consider the following script:

from Products.CMFCore.utils import getToolByName
urltool = getToolByName(context, "portal_url")
portal = urltool.getPortalObject()
document = getattr(portal, "my_document")
review_state = document.portal_workflow.getInfoFor(document, "review_state", "")
print "The initial state is: " + review_state + "\n"

if not review_state in ("rejected", "retracted", "private"):
document.portal_workflow.doActionFor(document, "hide", comment="")
review_state = document.portal_workflow.getInfoFor(document, "review_state", "")
print "The final state is: " + review_state + "\n"
return printed

We can observe the use of two methods:

  • getInfoFor(self, ob, name, default=[], wf_id=None, args, *kw) This is a method from CMFCore.WorkflowTool.WorkflowTool class. It returns an specific property (given by name) relative to a workflow for a given object ob.
  • doActionFor(self, ob, action, wf_id=None, args, *kw) Is a method from CMFCore.WorkflowTool.WorkflowTool class. It executes the action over a workflow for the given object ob.

Use Plone's catalog

Plone provides a tool (Catalog), that can be queried about content objects inside a given Portal.

In this section we're going to learn the searchResults method. First, the use of it is going to be explained, and secondly, an example of its common usage is going to be shown.

  • searchResults(self, REQUEST=None, **kw) This method is defined in CMFCore.CatalogTool.CatalogTool class, and its implemented thourgh a call to ZCatalog.searchResults method. Extra parameters are passed so results are limited according to the user's permissions. The paremeters define the query to be made to the catalog instance. The query can be a keyword, or a mapping, or part of a REQUEST object (tipically from an HTML document). The index of the catalog to be consulted is either the keyword's name, a mapping's key, or an attribute of a record object.
    • record objects' attributes
      • query: a sequence of objects or a single value to be passed as the query to the index (mandatory).
      • operator: specifies the combination of search results when query is a sequence of values (optional, default: OR). Possible values: AND, OR for Keyword Indexes and Path Indexes; AND, OR, ANDNOT, NEAR for Text Indexes.
      • range: defines a search range over an index (optional, default: off). Possible values: min to search for object with larger values than the maximun of the values passed in query, max same as min but searching for smaller values than the minimun, and minmax which is a combination of the formers.
      • level: only for Path indixes. It specifies the directory level in which start the search (optional, default: 0).

Example 7: how to list all documents ordered decreasingly by id.

The following script lists all documents in the current Portal ordered decreasingly by id. Please note that portal_type restrictes the type of the content objects, and sort_on and sort_order specify the requerid ordering.

results = context.portal_catalog.searchResults(sort_on="id",
portal_type="Document",
sort_order="reverse")
print [i.getObject().id for i in results]
return printed

Under Plone 2.1.1, if we'd want to order results by title, we should make the searchResults call setting sort_on parameter to sortable_title.

Please also note that the method getObject is called for each result obtained from the catalog. This is because the objects resulting of a catalog search are not content objects themself. As you migth guessed, getObject method returns the real content object.

Example 8: how to list all private objects that include texto word.

Each content object defines what part of its information is searchable. The parameter SearchableText searches for that information in every object. The parameter review_state filters objects according its state:

results = context.portal_catalog.searchResults(SearchableText="texto",
review_state="private")
print [i.getObject().Title() for i in results]
return printed

Example 9: how to list all objects with creation date after November, 30th, 2005 and with creation user admin.

This scripts shows how a record object can be passed to searchResults method. The Date parameter receives a record object to query for those objects created after a given date. The Creator parameter filters the objects that weren't created by admin user:

results = context.portal_catalog.searchResults
(Date={"query": DateTime("2005/11/30"), "range": "min"},
Creator="admin")
print [i.getObject().Title() for i in results]
return printed

Manipulate users and groups

In this section we describe how to use the Plone's API to create users, groups, and how to assign users to groups.

Example 10: how to add a user.

Consider the following:

id = "user"
fullname = "Emanuel Sartor"
password = "changeme"
email = "emanuel@menttes.com"
roles = ("Manager",)
status=""
props = {"username": id,
"fullname": fullname,
"password": password,
"email": email}
# add a new member to the Portal
try:
context.portal_registration.addMember(id, password, roles,domains="",
properties=props)
status+="The user "+fullname+" was successfully added.\n"
except:
status+="The user "+fullname+" was not added.\n"
print status
return printed
  • addMember(self, id, password, roles, domains, properties) Creates an returns a PortalMember.

More on RegistrationTool-class.html

Example 11: how to create a group named Group0.

Consider the following:

groupname = "Group0"
status=""
# add a new group
try:
context.portal_groups.addGroup(groupname,)
status += "The group was successfully added.\n"
except:
status += "Manager group couldn't be added\n"
print status
return printed
  • addGroup(self, id, roles, groups, args, *kw)

More on GroupsTool-class.html

Example 12: how to add a user to a group.

Consider the following:

id = "usuario"
groupname = "Group0"
status=""
# assign the user "user" to the group "groupname"
try:
group = context.portal_groups.getGroupById(groupname)
group.addMember(id)
status += "The user "+id+" was successfully added to "+groupname+".\n"
except:
status += "The user "+id+" was not added to group "+groupname+".\n"
print status
return printed

Exercices

Exercice 1:

Codify an script that receives two string arguments doc1 and doc2, and assume that this two parameters correspond to two document identifiers in the current folder. Make the script modify the content of doc2 so it'll hold the concatenation of doc1 and 'doc2's content.

Exercice 2:

Codify an script that:

  • Creates a folder with title Events in plone portal's root.
  • Moves all portal's objects with type Event to the newly created folder.
  • Changes the state of all portal's event to Published.

Help:

  • obj.aq_inner.aq_parent is the object's parent of obj.
  • obj.objectValues() lists all obj's children.
FairSource
Case Studies
More Case Studies

Copyright © 2004-2007

130 Boulevard NE, # 6 - Atlanta, GA 30312

Phone: 678-608-3408, E-mail: info@ifpeople.net

Personal tools