Content#

Create content#

To add an object, you must first have a container to put it in. Get the portal object; it will serve nicely:

from plone import api
portal = api.portal.get()

Create your new content item using the api.content.create() method. The type argument will decide which content type will be created.

from plone import api
obj = api.content.create(
    type='Document',
    title='My Content',
    container=portal)

The id of the new object is automatically and safely generated from its title.

self.assertEqual(obj.id, 'my-content')

Get content object#

There are several approaches to getting your content object. Consider the following portal structure:

plone (portal root)
|-- blog
|-- about
|   |-- team
|   `-- contact
`-- events
    |-- training
    |-- conference
    `-- sprint

The following operations will get objects from the stucture above, including using api.content.get().

# let's first get the portal object
from plone import api
portal = api.portal.get()
assert portal.id == 'plone'

# content can be accessed directly with dict-like access
blog = portal['blog']

# another way is to use ``get()`` method and pass it a path
about = api.content.get(path='/about')

# more examples
conference = portal['events']['conference']
sprint = api.content.get(path='/events/sprint')

# moreover, you can access content by its UID
uid = about['team'].UID()
team = api.content.get(UID=uid)

# returns None if UID cannot be found in catalog
not_found = api.content.get(UID='notfound')

Find content objects#

You can use the find function to search for content.

Finding all Documents:

from plone import api
documents = api.content.find(portal_type='Document')

Finding all Documents within a context:

from plone import api
documents = api.content.find(
    context=api.portal.get(), portal_type='Document')

Limit search depth:

from plone import api
documents = api.content.find(depth=1, portal_type='Document')

Limit search depth within a context:

from plone import api
documents = api.content.find(
    context=api.portal.get(), depth=1, portal_type='Document')

Search by interface:

from plone import api
from Products.CMFCore.interfaces import IContentish
documents = api.content.find(object_provides=IContentish)

Combining multiple arguments:

from plone import api
from Products.CMFCore.interfaces import IContentish
documents = api.content.find(
    context=api.portal.get(),
    depth=2,
    object_provides=IContentish,
    SearchableText='Team',
)

More information about how to use the catalog may be found in the Plone Documentation.

Note that the catalog returns brains (metadata stored in indexes) and not objects. However, calling getObject() on brains does in fact give you the object.

document_brain = documents[0]
document_obj = document_brain.getObject()

Get content object UUID#

A Universally Unique IDentifier (UUID) is a unique, non-human-readable identifier for a content object which remains constant for the object even if the object is moved.

Plone uses UUIDs for storing references between content and for linking by UIDs, enabling persistent links.

To get the UUID of any content object use api.content.get_uuid(). The following code gets the UUID of the contact document.

from plone import api
portal = api.portal.get()
contact = portal['about']['contact']

uuid = api.content.get_uuid(obj=contact)

Move content#

To move content around the portal structure defined above use the api.content.move() method. The code below moves the contact item (with all it contains) out of the folder about and into the Plone portal root.

from plone import api
portal = api.portal.get()
contact = portal['about']['contact']

api.content.move(source=contact, target=portal)

Actually, move behaves like a filesystem move. If you pass it an id argument, the object will have that new ID in its new home. By default it will retain its original ID.

Rename content#

To rename a content object (change its ID), use the api.content.rename() method.

from plone import api
portal = api.portal.get()
api.content.rename(obj=portal['blog'], new_id='old-blog')

Copy content#

To copy a content object, use the api.content.copy() method.

from plone import api
portal = api.portal.get()
training = portal['events']['training']

api.content.copy(source=training, target=portal)

Note that the new object will have the same ID as the old object (unless otherwise stated). This is not a problem, since the new object is in a different container.

You can also set target to source's container and set safe_id=True. This will duplicate your content object in the same container and assign it a new, non-conflicting ID.

api.content.copy(source=portal['training'], target=portal, safe_id=True)
new_training = portal['copy_of_training']

Delete content#

To delete a content object, pass the object to the api.content.delete() method:

from plone import api
portal = api.portal.get()
api.content.delete(obj=portal['copy_of_training'])

To delete multiple content objects, pass the objects to the api.content.delete() method:

from plone import api
portal = api.portal.get()
data = [portal['copy_of_training'], portal['events']['copy_of_training'], ]
api.content.delete(objects=data)

If deleting content would result in broken links you will get a LinkIntegrityNotificationException. To delete anyway, set the option check_linkintegrity to False:

from plone import api
portal = api.portal.get()
api.content.delete(obj=portal['copy_of_training'], check_linkintegrity=False)

Content manipulation with the safe_id option#

When you manipulate content with api.content.create(), api.content.move() or api.content.copy() the safe_id flag is disabled by default. This means the uniqueness of IDs will be enforced. If another object with the same ID is already present in the target container these API methods will raise an error.

However, if the safe_id option is enabled, a non-conflicting ID will be generated.

api.content.create(container=portal, type='Document', id='document', safe_id=True)
document = portal['document-1']

Get workflow state#

To find out the current workflow state of your content, use the api.content.get_state() method.

from plone import api
portal = api.portal.get()
state = api.content.get_state(obj=portal['about'])

The optional default argument is returned if no workflow is defined for the object.

from plone import api
portal = api.portal.get()
state = api.content.get_state(obj=portal['image'], default='Unknown')

Transition#

To transition your content to a new workflow state, use the api.content.transition() method.

from plone import api
portal = api.portal.get()
api.content.transition(obj=portal['about'], transition='publish')

If your workflow accepts any additional arguments to the checkin method you may supply them via kwargs. These arguments can be saved to your transition using custom workflow variables inside the ZMI using an expression such as "python:state_change.kwargs.get('comment', '')"

from plone import api
portal = api.portal.get()
api.content.transition(obj=portal['about'], transition='reject', comment='You had a typo on your page.')

Disable local roles acquisition#

To disable the acquisition of local roles for an object, use the api.content.disable_roles_acquisition() method.

from plone import api
portal = api.portal.get()
api.content.disable_roles_acquisition(obj=portal['about'])

Enable local roles acquisition#

To enable the acquisition of local roles for an object, use the api.content.enable_roles_acquisition() method.

from plone import api
portal = api.portal.get()
api.content.enable_roles_acquisition(obj=portal['about'])

Get view#

To get a BrowserView for your content, use api.content.get_view().

from plone import api
portal = api.portal.get()
view = api.content.get_view(
    name='plone',
    context=portal['about'],
    request=request,
)

Since version 2.0.0, the request argument can be omitted. In that case the global request will be used.

from plone import api
portal = api.portal.get()
view = api.content.get_view(
    name='plone',
    context=portal['about'],
)

Further reading#

For more information on possible flags and usage options please see the full plone.api.content specification.