- Common interfaces
- Implementing one or multiple interfaces
- Checking whether object provides an interface
- Getting interface string id
- Getting interface class by its string id
- Applying interfaces for several content types
- Dynamic marker interfaces
- Tagged values
Interfaces define what methods an object provides. Plone extensively uses interfaces to define APIs between different subsystems. They provide a more consistent and declarative way to define bridges between two different things, when duck-typing is not enough.
An interface defines the shape of a hole where different pieces fit. The shape of the piece is defined by the interface, but the implementation details like color, material, etc. can vary.
Some interfaces are commonly used throughout Plone.
The usual use case is that a context directive for a view is provided, specifying where the view is available (e.g. for which content types).
- Base class of all interfaces. Also used as a
*wildcard when registering views, meaning that the view applies on every object.
- All content items on the site.
In the site root, this interface excludes Zope objects like
acl_users(the user folder) and
portal_skinswhich might otherwise appear in the item listing when you iterate through the root content.
- All folders in the site.
- The Plone site root object.
plone.app.layout.navigation.interfaces import INavigationRoot
- Navigation top object - where the breadcrumbs are anchored. On multilingual sites, this is the top-level folder for the current language.
zope.interface.implements() in your class body.
Multiple interfaces can be provided as arguments.
from zope.interface import implements from collective.mountpoint.interfaces import ILocalSyncedContent from ora.objects.interfaces import IORAResearcher class MyContent(folder.ATFolder): """A Researcher synchronized from ORA""" implements(IORAResearcher, ILocalSyncedContent)
In Python you can use code:
from yourpackage.interfaces import IMyInterface if IMyInterface.providedBy(object): # do stuff else: # was not the kind of object we wanted
In page templates you can use
plone_interface_info helper view:
<div tal:define="iinfo context/@@plone_interface_info"> <span tal:condition="python:iinfo.provides('your.dotted.interface.IName')"> Do stuff requiring your interface. </span> </div>
Interface resolution order (IRO) is the list of interfaces provided by the object (directly, or implemented by a class), sorted by priority.
Interfaces are evaluated from zero index (highest priority) to the last index (lowest priority).
You can access this information for the object for debugging purposes using a magical attribute:
Since adapter factories are dynamic (adapter interfaces not hardcoded
on the object), the object can still adapt to interfaces which are not
The interface id is stored in the
import zope.interface class IFoo(zope.interface.Interface). pass # id is yourpackage.interfaces.IFoo id = IFoo.__identifier__
Note that this attribute does not respect import aliasing.
Use the zope.dottedname package.
import zope.interface from zope.dottedname.resolve import resolve class IFoo(zope.interface.Interface). pass # id is yourpackage.interfaces.IFoo id = IFoo.__identifier__ interface_class == resolve(id) assert IFoo == interface_class
You can apply marker interfaces to content types at any time.
Example use cases:
- You want to assign a viewlet to a set of particular content types.
- You want to enable certain behavior on certain content types.
A marker interface is needed only when you need to create a common
nominator for several otherwise unrelated classes.
You can use one existing class or interface as a context without
explicitly creating a marker interface.
zope.interface.Interface as a context
usually accept a normal Python class as well (
You can assign the marker interface for several classes in ZCML using
<class> declaration. Here we're assigning
to documents, events and news items:
<!-- List of content types where "last modified" viewlet is enabled --> <class class="Products.ATContentTypes.content.document.ATDocument"> <implements interface=".interfaces.ILastModifiedSupport" /> </class> <class class="Products.ATContentTypes.content.event.ATEvent"> <implements interface=".interfaces.ILastModifiedSupport" /> </class> <class class="Products.ATContentTypes.content.newsitem.ATNewsItem"> <implements interface=".interfaces.ILastModifiedSupport" /> </class>
Then we can have a view for these content types only using the following:
.. code-block:: python
from Products.Five import BrowserView from interfaces import ILastModifiedSupport from plone.app.layout.viewlets.interfaces import IBelowContent
- class LastModified(BrowserView):
- """ View for .interfaces.ILastModifiedSupport only """
<browser:view for=".interfaces.ILastModifiedSupport" name="lastmodified" class=".views.LastModified" template="templates/lastmodified.pt" />
- zope.dottedname allows you to resolve dotted names to Python objects manually
Zope allows to you to dynamically turn on and off interfaces on any content objects through the ZMI. Browse to any object and visit the Interfaces tab.
Marker interfaces might need to be explicitly declared using the
<interface> directive, so that Zope can find them:
<!-- Declare marker interface, so that it is available in ZMI --> <interface interface="mfabrik.app.interfaces.promotion.IPromotionsPage" />
The interface dotted name must refer directly to the interface class and
not to an import from other module, like
mark() function from Products.Five.
from Products.Five.utilities.marker import mark mark(portal.doc, interfaces.IBuyableMarker)
This marking persists with the object: it is not temporary.
Under the hood:
mark() delegates to
zope.interface.directlyProvides() — with
the result that
a persistent object (e.g. content item) has a reference to the interface
class you mark it with in its
__provides__ attribute; this attribute
serialized and loaded by ZODB like any other reference to a class, and
zope.interface uses object specification descriptor magic (just like
for any other object, persistent or not) to resolve provided interfaces.
To remove a marker interface from an object, use the
from Products.Five.utilities.marker import erase erase(portal.doc, interfaces.IBuyableMarker)