Skip to main content

An activity feed for Plone.

Project description


ftw.activity provides a view with an activity stream for Plone.

How it works

Activities are stored with event handlers into a custom souper catalog. An activity view then renders each activity for a context (recursively) with activity renderers.

Supported events

The default event handlers work for Archetypes and Dexterity objects.

  • Object added

  • Object changed

  • Object deleted

  • Object copied

  • Object moved

  • Workflow transition


  • Add ftw.activity as dependency to your package ( or to your buildout configuration:

eggs +=
  • Install the generic import profile in Plone’s addons control panel.

Once the package is installed there is no link to the view. The view is available as /activity on any context, so you might want to place a link anywhere you like or add an action.

ftw.activity also registers an ftw.tabbedview tab with the name tabbedview_view-activity.

Custom activities

Custom activities can easily be registered in the souper catalog and are automatically rendered:

from ftw.activity.catalog import ActivityRecord
from ftw.activity.catalog import get_activity_soup

record = ActivityRecord(context, 'downloaded')

Activity renderers

The default activity renderer renders the activity with a link to the object (unless it was deleted), the event and the actor.

However, if you want to change how activities are rendered you can easily do that with a custom renderer. An activity renderer is a named multi-adapter.

Be aware that the renderer adapts the context where the activity view is rendered, not the object on which the activity happened. The reason for that is that the object may no longer exist.

The renderer must implement three methods, position, match and render. Since there may be multiple adapters which can render an activity, the position is used to determine which renderer precedes. The match method is used to ask the renderer whether he wants to render a certain activity. If the activity matches, it is renderered using the render method.

Warning Be aware the the object passed to match and render may be None, when the object was deleted.

Example ZCML registration:

<adapter factory=".activity.CustomActivityRenderer" name="my.package-renderer" />

Implement the adapter (

from ftw.activity.interfaces import IActivityRenderer
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
from zope.component import adapts
from zope.interface import implements
from zope.interface import Interface

class CustomActivityRenderer(object):
    adapts(Interface, Interface, Interface)

    index = ViewPageTemplateFile('templates/')

    def __init__(self, context, request, view):
        self.context = context
        self.request = request
        self.view = view
        self.items = []

    def position(self):
        # The position of the default renderer is 1000
        return 500

    def match(self, activity, obj):
        return activity.attrs['portal_type'] == 'MyType'

    def render(self, activity, obj):
        return self.index(activity=activity, obj=obj)

In the template (templates/ you may want to use the default activity macro and extend it:

<metal:wrapper use-macro="context/@@activity_macros/macros/event">
  <metal:CONTENT fill-slot="body-content"
                 tal:define="activity nocall:activity|nocall:options/activity">

    <div tal:attributes="class string:activity-icon-{$activity/action}"></div>


Store additional information on activities

The metadata stored on the activity record can be easily extended with an event handler:

def enhance_activity_record(event):
    record = event.activity
    obj = event.object
    record.attrs['creator'] = obj.Creator()

Activity Filters

There may be various custom use cases which require activities to be dropped when rendering the activity view. For example system activities are recorded for internal purposes which should not show up in the activity feed.

Activities can easily be filtered by registering an IActivityFilter adapter:

<adapter factory=".activity.CustomActivityFilter" name="my.package-filter" />
from ftw.activity.interfaces import IActivityFilter
from zope.component import adapts
from zope.interface import implements
from zope.interface import Interface

class CustomActivityFilter(object):
    adapts(Interface, Interface, Interface)

    def __init__(self, context, request, view):
        self.context = context
        self.request = request
        self.view = view

    def position(self):
        return 500

    def process(self, activities):
        for activity in activities:
            if activity.attrs['action'] == 'custom-action':

            yield activity

There is a default FilterCloseChanges filter, which removes succeding “changed” activites of the same object which happen in less than 1 Minute between each activity. This is removes noise from the activity feed when a user edits the same object a lot in short time, for example when using an external editor.

Local activity view

The @@local-activity view is available on any context and shows only activities of the current context but not its children.

Hint about Plone 5.1 testing using ftw.testing.freeze

For some reason there is a transaction.commit() required right before using freeze (contextmanager). Details might be found in I think it’s a similar problem and maybe the transaction.commit() right before using the freeze contextmanager might circumvent the issues accidentally.


2.5.2 (2021-02-26)

  • Show plone toolbar in activity view [Nachtalb]

2.5.1 (2019-12-14)

  • Provide precompiled bundle. [tinagerber]

2.5.0 (2019-11-11)

  • Upgrade to ftw.theming (also fixes missing css on plone 5) [Nachtalb]

2.4.0 (2019-10-29)

  • Plone 5.1 support [mathias.leimgruber]

  • Ignore events fired by moving a plone site [raphael-s]

2.3.2 (2017-07-03)

  • Resize image in readme to fix a bug with its size on pypi [raphael-s]

  • Ignore events of objects without UIDs to avoid errors. [raphael-s]

2.3.1 (2017-06-19)

  • Add version constraint for node.ext.zodb <= 1.0.1 to avoid pulling in ZODB 5. [lgraf]

2.3.0 (2016-11-23)

  • Store activites for added / removed p.a.discussion comments. [jone]

2.2.2 (2016-11-15)

  • Fix error in detection of last event (More button). [jone]

2.2.1 (2016-11-15)

  • Hide “More” button when there are no more events. [jone]

  • Hide “More” button when there are no events. [jone]

2.2.0 (2016-06-01)

  • Optimize performance of date index by storing timestamps. [jone]

2.1.0 (2016-03-21)

  • Fix bug with collective.geo when indexing objects too early. [jone]

  • Fix a bug which occurred when moving objects from and to the Plone Site. [mbaechtold]

  • Fix fetching within tabbedview tabs. [jone]

  • Add an Activity Portlet [elioschmutz]

2.0.0 (2015-09-30)

  • Implement custom souper-based catalog for logging all activities separately. This allows us to also track delete activities. The rendering has changed completely: custom representations must be rewritten to the new renderer adapter. [jone]

  • Remove support for collections. Because the activity feed will no longer be catalog based. [jone]

1.1.5 (2015-05-18)

  • Fix diazo error when fetching more events. [jone]

  • disable diazo themeing for ajax responses. [jone]

1.1.4 (2015-03-25)

  • Fix width of too wide images. [Kevin Bieri]

1.1.3 (2014-11-18)

  • Fixes a bug where the activity view crashed when the modifying user is no longer available. [mbaechtold]

1.1.2 (2014-09-24)

  • Ignore comments in activity view. Fixes a bug where the activity view crashed when comments were added. [jone]

1.1.1 (2014-09-24)

  • Empty brown-bag release.

1.1.0 (2014-09-04)

  • Add support for collections. [jone]

1.0.0 (2014-09-03)

  • Initial implementation.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ftw.activity-2.5.2.tar.gz (79.7 kB view hashes)

Uploaded source

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor NVIDIA NVIDIA PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page