Skip to content

Commit

Permalink
Add support for logging workflow transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
hvelarde committed Jan 25, 2017
1 parent 2c81ea0 commit 49d8b2f
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 6 deletions.
6 changes: 5 additions & 1 deletion CHANGES.rst
Expand Up @@ -4,7 +4,11 @@ Changelog
1.2b2 (unreleased)
------------------

- Avoid ComponentLookupError when plonectl adduser.
- Add support for logging workflow transitions.
[hvelarde]

- Avoid ComponentLookupError when plonectl adduser.
[jianaijun]


1.2b1 (2016-09-28)
Expand Down
6 changes: 6 additions & 0 deletions src/collective/fingerpointing/interfaces.py
Expand Up @@ -27,6 +27,12 @@ class IFingerPointingSettings(Interface):
default=True,
)

audit_workflow = schema.Bool(
title=_(u'Audit Workflow Transitions?'),
description=_(u'Log workflow transitions like object publishing.'),
default=True,
)

audit_registry = schema.Bool(
title=_(u'Audit Registry?'),
description=_(u'Log registry events like records being modified.'),
Expand Down
@@ -1,4 +1,4 @@
<?xml version="1.0"?>
<metadata>
<version>3</version>
<version>4</version>
</metadata>
1 change: 1 addition & 0 deletions src/collective/fingerpointing/subscribers/__init__.py
Expand Up @@ -3,3 +3,4 @@
from .lifecycle_logger import lifecycle_logger # noqa
from .pas_logger import pas_logger # noqa
from .registry_logger import registry_logger # noqa
from .workflow_logger import workflow_logger # noqa: F401
6 changes: 6 additions & 0 deletions src/collective/fingerpointing/subscribers/configure.zcml
Expand Up @@ -97,4 +97,10 @@
handler=".iterate_logger"
/>

<!-- Workflow -->
<subscriber
for="Products.CMFCore.interfaces.IActionSucceededEvent"
handler=".workflow_logger"
/>

</configure>
23 changes: 23 additions & 0 deletions src/collective/fingerpointing/subscribers/workflow_logger.py
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
"""Event subscriber handler for IActionSucceededEvent."""
from collective.fingerpointing.config import AUDIT_MESSAGE
from collective.fingerpointing.interfaces import IFingerPointingSettings
from collective.fingerpointing.logger import log_info
from collective.fingerpointing.utils import get_request_information
from plone import api
from plone.api.exc import InvalidParameterError


def workflow_logger(event):
"""Log workflow transitions."""
try:
record = IFingerPointingSettings.__identifier__ + '.audit_workflow'
audit_workflow = api.portal.get_registry_record(record)
except InvalidParameterError:
return # package is not installed

if audit_workflow:
user, ip = get_request_information()
action = 'workflow transition'
extras = u'object={0} transition={1}'.format(event.object, event.action)
log_info(AUDIT_MESSAGE.format(user, ip, action, extras))
7 changes: 3 additions & 4 deletions src/collective/fingerpointing/testing.py
Expand Up @@ -43,17 +43,16 @@ def setUpZope(self, app, configurationContext):

def setUpPloneSite(self, portal):
self.applyProfile(portal, 'collective.fingerpointing:default')
portal.portal_workflow.setDefaultChain('simple_publication_workflow')


FIXTURE = Fixture()

INTEGRATION_TESTING = IntegrationTesting(
bases=(FIXTURE,),
name='collective.fingerpointing:Integration')
bases=(FIXTURE,), name='collective.fingerpointing:Integration')

FUNCTIONAL_TESTING = FunctionalTesting(
bases=(FIXTURE,),
name='collective.fingerpointing:Functional')
bases=(FIXTURE,), name='collective.fingerpointing:Functional')

ROBOT_TESTING = FunctionalTesting(
bases=(FIXTURE, AUTOLOGIN_LIBRARY_FIXTURE, z2.ZSERVER_FIXTURE),
Expand Down
5 changes: 5 additions & 0 deletions src/collective/fingerpointing/tests/test_controlpanel.py
Expand Up @@ -66,6 +66,10 @@ def test_audit_lifecycle_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'audit_lifecycle'))
self.assertEqual(self.settings.audit_lifecycle, True)

def test_audit_workflow_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'audit_workflow'))
self.assertEqual(self.settings.audit_workflow, True)

def test_audit_registry_record_in_registry(self):
self.assertTrue(hasattr(self.settings, 'audit_registry'))
self.assertEqual(self.settings.audit_registry, True)
Expand All @@ -83,6 +87,7 @@ def test_records_removed_on_uninstall(self):
records = [
IFingerPointingSettings.__identifier__ + '.audit_pas',
IFingerPointingSettings.__identifier__ + '.audit_lifecycle',
IFingerPointingSettings.__identifier__ + '.audit_workflow',
IFingerPointingSettings.__identifier__ + '.audit_registry',
IFingerPointingSettings.__identifier__ + '.audit_iterate',
]
Expand Down
38 changes: 38 additions & 0 deletions src/collective/fingerpointing/tests/test_upgrades.py
Expand Up @@ -120,3 +120,41 @@ def test_update_user_actions(self):
self._do_upgrade(step)
self.assertEqual(user_actions.keys()[-1], 'logout')
self.assertEqual(user_actions.keys()[-2], 'audit-log')


class To4TestCase(BaseUpgradeTestCase):

from_ = '3'
to_ = '4'

def test_profile_version(self):
version = self.setup.getLastVersionForProfile(self.profile_id)[0]
self.assertEqual(version, self.from_)

def test_registered_steps(self):
steps = len(self.setup.listUpgrades(self.profile_id)[0])
self.assertEqual(steps, 1)

def test_update_user_actions(self):
# check if the upgrade step is registered
title = u'Add new field audit_workflow to configlet'
step = self._get_upgrade_step_by_title(title)
assert step is not None

from collective.fingerpointing.interfaces import IFingerPointingSettings
from plone.registry.interfaces import IRegistry
from zope.component import getUtility
registry = getUtility(IRegistry)

# simulate state on previous version
record = IFingerPointingSettings.__identifier__ + '.audit_workflow'
del registry.records[record]

with self.assertRaises(KeyError):
registry.forInterface(IFingerPointingSettings)

# execute upgrade step and verify changes were applied
self._do_upgrade(step)
settings = registry.forInterface(IFingerPointingSettings)
self.assertTrue(hasattr(settings, 'audit_workflow'))
self.assertEqual(settings.audit_workflow, True)
51 changes: 51 additions & 0 deletions src/collective/fingerpointing/tests/test_workflow_subscriber.py
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
"""Tests for workflow subscriber."""
from collective.fingerpointing.config import PROJECTNAME
from collective.fingerpointing.interfaces import IFingerPointingSettings
from collective.fingerpointing.testing import INTEGRATION_TESTING
from logging import INFO
from plone import api
from plone.app.testing import setRoles
from plone.app.testing import TEST_USER_ID
from testfixtures import LogCapture

import unittest


class WorkflowSubscribersTestCase(unittest.TestCase):

"""Tests content type life cycle subscribers."""

layer = INTEGRATION_TESTING

def setUp(self):
self.portal = self.layer['portal']
setRoles(self.portal, TEST_USER_ID, ['Manager'])

# disable lifecycle audit
record = IFingerPointingSettings.__identifier__ + '.audit_lifecycle'
api.portal.set_registry_record(record, False)

def test_workflow_transitions(self):
expected = (
('collective.fingerpointing', 'INFO', u'user=test ip=127.0.0.1 action=workflow transition object=<NewsItem at foo> transition=submit'),
('collective.fingerpointing', 'INFO', u'user=test ip=127.0.0.1 action=workflow transition object=<NewsItem at foo> transition=publish'),
('collective.fingerpointing', 'INFO', u'user=test ip=127.0.0.1 action=workflow transition object=<NewsItem at foo> transition=retract'),
)

with LogCapture('collective.fingerpointing', level=INFO) as log:
obj = api.content.create(self.portal, 'News Item', 'foo')
api.content.transition(obj=obj, transition='submit')
api.content.transition(obj=obj, transition='publish')
api.content.transition(obj=obj, transition='retract')
log.check(*expected)

def test_susbcriber_ignored_when_package_not_installed(self):
# events should not raise errors if package is not installed
qi = self.portal['portal_quickinstaller']
qi.uninstallProducts(products=[PROJECTNAME])

obj = api.content.create(self.portal, 'News Item', 'foo')
api.content.transition(obj=obj, transition='submit')
api.content.transition(obj=obj, transition='publish')
api.content.transition(obj=obj, transition='retract')
1 change: 1 addition & 0 deletions src/collective/fingerpointing/upgrades/configure.zcml
@@ -1,4 +1,5 @@
<configure xmlns="http://namespaces.zope.org/zope">
<include package=".v2" />
<include package=".v3" />
<include package=".v4" />
</configure>
1 change: 1 addition & 0 deletions src/collective/fingerpointing/upgrades/v4/__init__.py
@@ -0,0 +1 @@
# -*- coding: utf-8 -*-
19 changes: 19 additions & 0 deletions src/collective/fingerpointing/upgrades/v4/configure.zcml
@@ -0,0 +1,19 @@
<configure
xmlns="http://namespaces.zope.org/zope"
xmlns:genericsetup="http://namespaces.zope.org/genericsetup">

<genericsetup:upgradeSteps
source="3"
destination="4"
profile="collective.fingerpointing:default">

<genericsetup:upgradeDepends
title="Add new field audit_workflow to configlet"
description="Reload registration of configlet registry to add new field."
import_steps="plone.app.registry"
run_deps="false"
/>

</genericsetup:upgradeSteps>

</configure>

0 comments on commit 49d8b2f

Please sign in to comment.