[Products.Silva][Sylvain Viollon] Change how modification date a...

[email protected] Fri, 30 Aug 2013 15:59:16 +0200
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Fri Aug 30 15:59:11 2013 +0200
revision:  11854:caf1cc5b1bd4 in Products.Silva
branch:    2.4
details:   https://hg.infrae.com/Products.Silva?cmd=changeset;node=caf1cc5b1bd4
modified:  Products/Silva/EmailMessageService.py Products/Silva/Security/__init__.py Products/Silva/Security/changes.py Products/Silva/Security/content.py Products/Silva/SilvaObject.py Products/Silva/Version.py Products/Silva/VersionedContent/cataloging.py Products/Silva/testing.py Products/Silva/tests/test_file.py Products/Silva/tests/test_folder.py Products/Silva/tests/test_ghost.py Products/Silva/tests/test_image.py Products/Silva/tests/test_versioning_metadata.py Products/Silva/tests/test_xml_export.py
added:     Products/Silva/Security/changes.py
removed:   
log:       Change how modification date and last author are set.


diffstat:

 Products/Silva/EmailMessageService.py            |    3 +
 Products/Silva/Security/__init__.py              |    1 +
 Products/Silva/Security/changes.py               |   85 ++++++++
 Products/Silva/Security/content.py               |   20 +-
 Products/Silva/SilvaObject.py                    |   11 +-
 Products/Silva/Version.py                        |   19 +-
 Products/Silva/VersionedContent/cataloging.py    |   11 +-
 Products/Silva/testing.py                        |   12 +-
 Products/Silva/tests/test_file.py                |   28 +-
 Products/Silva/tests/test_folder.py              |   14 +-
 Products/Silva/tests/test_ghost.py               |   84 +++++---
 Products/Silva/tests/test_image.py               |  115 ++++++-----
 Products/Silva/tests/test_versioning_metadata.py |   28 +-
 Products/Silva/tests/test_xml_export.py          |  230 ++++++++++++----------
 14 files changed, 396 insertions(+), 265 deletions(-)

diffs (1224 lines):

diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/EmailMessageService.py
--- a/Products/Silva/EmailMessageService.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/EmailMessageService.py	Fri Aug 30 15:59:11 2013 +0200
@@ -30,6 +30,9 @@
 logger = logging.getLogger('silva.email')
 
 
+# XXX This should be updated to use silva.core.services.delayed
+
+
 class EmailQueueSavepoint(object):
     grok.implements(IDataManagerSavepoint)
 
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/Security/__init__.py
--- a/Products/Silva/Security/__init__.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/Security/__init__.py	Fri Aug 30 15:59:11 2013 +0200
@@ -3,3 +3,4 @@
 # See also LICENSE.txt
 
 from Products.Silva.Security.content import Security
+from Products.Silva.Security.changes import ChangesTask
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/Security/changes.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Products/Silva/Security/changes.py	Fri Aug 30 15:59:11 2013 +0200
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+
+from AccessControl import getSecurityManager
+from DateTime import DateTime
+
+from five import grok
+from silva.core.interfaces import IUpgradeTransaction
+from silva.core.services.delayed import Task
+from silva.core.services.interfaces import IMemberService, IMetadataService
+from zope.component import getUtility
+
+
+class Key(object):
+    __slots__ = ('content', 'hash')
+
+    def __init__(self, content):
+        self.content = content
+        self.hash = hash((content._p_oid, content._p_jar._db.database_name))
+
+    def __eq__(self, other):
+        return other.content == self.content
+
+    def __hash__(self):
+        return self.hash
+
+
+class ChangesTask(Task):
+    """Task in charge of updating the modification date and last
+    author in Silva.
+
+    Of course this assume that all the transaction is executed by the
+    same author, that is the case in Zope.
+    """
+    priority = 0                # It must be one of the first things to be done.
+
+    def __init__(self, enabled=True, changes=None):
+        self._enabled = enabled
+        self._changes = {} if changes is None else changes.copy()
+
+    def copy(self):
+        return ChangesTask(self._enabled, self._changes)
+
+    def disable(self):
+        # We want to disable this feature during upgrade.
+        self._enabled = False
+
+    def modified(self, content, created=False):
+        # Content have been modified.
+        if self._enabled:
+            key = Key(content)
+            if created or key not in self._changes:
+                self._changes[key] = created
+
+    def finish(self):
+        if not self._enabled or not self._changes:
+            # Don't do things.
+            pass
+
+        now = DateTime()
+        login = getSecurityManager().getUser().getId()
+        members = getUtility(IMemberService)
+        metadata = getUtility(IMetadataService)
+
+        for change, created in self._changes.iteritems():
+            # Update author
+            # XXX This could probably be done outside of the loop
+            user = members.get_cached_member(login, location=change.content)
+            change.content.set_last_author_info(user)
+
+            # Update metadata
+            binding = metadata.getMetadata(change.content)
+            if binding is None or binding.read_only:
+                continue
+            values = {'modificationtime': now}
+            if created:
+                values['creationtime'] = now
+            binding.setValues('silva-extra', values)
+
+
[email protected](IUpgradeTransaction)
+def disable_upgrade(event):
+    ChangesTask.get().disable()
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/Security/content.py
--- a/Products/Silva/Security/content.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/Security/content.py	Fri Aug 30 15:59:11 2013 +0200
@@ -3,18 +3,16 @@
 # See also LICENSE.txt
 
 # Zope
-from AccessControl import ClassSecurityInfo, getSecurityManager
+from AccessControl import ClassSecurityInfo
 from Acquisition import aq_base
 from App.class_init import InitializeClass
-from DateTime import DateTime
 
+from five import grok
 from zope.component import getUtility
-from five import grok
 
 # Silva
 from Products.Silva.Membership import noneMember
 from Products.Silva import SilvaPermissions as permissions
-from Products.SilvaMetadata.interfaces import IMetadataService
 
 from silva.core.interfaces import ISecurity
 from silva.core.services.interfaces import IMemberService
@@ -49,18 +47,10 @@
         return info.__of__(self)
 
     security.declareProtected(
-        permissions.ChangeSilvaContent, 'update_last_author_info')
-    def update_last_author_info(self):
-        user_id = getSecurityManager().getUser().getId()
-        user = getUtility(IMemberService).get_cached_member(
-            user_id, location=self)
-        self._last_author_userid = user_id
+        permissions.ChangeSilvaContent, 'set_last_author_info')
+    def set_last_author_info(self, user):
+        self._last_author_userid = user.userid()
         self._last_author_info = aq_base(user)
-        binding = getUtility(IMetadataService).getMetadata(self)
-        if binding is None or binding.read_only:
-            return
-        now = DateTime()
-        binding.setValues('silva-extra', {'modificationtime': now})
 
 
 InitializeClass(Security)
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/SilvaObject.py
--- a/Products/Silva/SilvaObject.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/SilvaObject.py	Fri Aug 30 15:59:11 2013 +0200
@@ -16,14 +16,13 @@
 # Zope 2
 from AccessControl import ClassSecurityInfo
 from App.class_init import InitializeClass
-from DateTime import DateTime
 from OFS.interfaces import IObjectClonedEvent
 from OFS.interfaces import IObjectWillBeAddedEvent
 from OFS.interfaces import IObjectWillBeMovedEvent
 
 # Silva
 from Products.Silva import SilvaPermissions
-from Products.Silva.Security import Security
+from Products.Silva.Security import Security, ChangesTask
 
 # Silva adapters
 from silva.core.interfaces import ISilvaObject, IVersionedContent
@@ -190,11 +189,7 @@
         return
 
     ICataloging(content).index()
-    service = getUtility(IMetadataService)
-    binding = service.getMetadata(content)
-    if binding is not None and not binding.read_only:
-        binding.setValues('silva-extra', {'creationtime': DateTime()})
-    content.update_last_author_info()
+    ChangesTask.get().modified(content, created=True)
 
 
 @grok.subscribe(ISilvaObject, IObjectModifiedEvent)
@@ -209,7 +204,7 @@
         return
     if getattr(content, '__initialization__', False):
         return
-    content.update_last_author_info()
+    ChangesTask.get().modified(content)
     ICataloging(content).reindex()
 
 
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/Version.py
--- a/Products/Silva/Version.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/Version.py	Fri Aug 30 15:59:11 2013 +0200
@@ -13,7 +13,6 @@
 # Zope 2
 from AccessControl import ClassSecurityInfo
 from App.class_init import InitializeClass
-from DateTime import DateTime
 from OFS.SimpleItem import SimpleItem
 from OFS.interfaces import IObjectWillBeRemovedEvent
 
@@ -21,7 +20,7 @@
 from Products.Silva import SilvaPermissions
 from Products.Silva.SilvaObject import TitledObject
 from Products.SilvaMetadata.interfaces import IMetadataService
-from Products.Silva.Security import Security
+from Products.Silva.Security import Security, ChangesTask
 
 from silva.core.interfaces import IVersion, VersioningError
 from silva.core.interfaces import IVersionManager
@@ -179,13 +178,9 @@
     if IObjectCopiedEvent.providedBy(event):
         return
 
-    if version == event.object:
-        service = getUtility(IMetadataService)
-        binding = service.getMetadata(version)
-        if binding is not None and not binding.read_only:
-            binding.setValues('silva-extra', {'creationtime': DateTime()})
-    version.update_last_author_info()
-    if version == event.object:
+    created = version == event.object
+    ChangesTask.get().modified(version, created)
+    if created:
         ICataloging(version).index()
         ICataloging(version.get_silva_object()).index(with_versions=False)
 
@@ -193,14 +188,14 @@
 @grok.subscribe(IVersion, IApprovalEvent)
 @grok.subscribe(IVersion, IContentPublishedEvent)
 def version_published(version, event):
-    version.update_last_author_info()
+    ChangesTask.get().modified(version)
     ICataloging(version).reindex()
     ICataloging(version.get_silva_object()).reindex(with_versions=False)
 
 
 @grok.subscribe(IVersion, IContentClosedEvent)
 def version_closed(version, event):
-    version.update_last_author_info()
+    ChangesTask.get().modified(version)
     ICataloging(version).unindex()
     ICataloging(version.get_silva_object()).reindex(with_versions=False)
 
@@ -209,7 +204,7 @@
 def version_modified(version, event):
     if not IPublishingEvent.providedBy(event):
         # This version have been modified
-        version.update_last_author_info()
+        ChangesTask.get().modified(version)
         ICataloging(version).reindex()
         ICataloging(version.get_silva_object()).reindex(with_versions=False)
 
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/VersionedContent/cataloging.py
--- a/Products/Silva/VersionedContent/cataloging.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/VersionedContent/cataloging.py	Fri Aug 30 15:59:11 2013 +0200
@@ -8,7 +8,7 @@
 from Acquisition import aq_base
 
 from silva.core.interfaces import IVersionedObject
-from silva.core.services.catalog import Cataloging, task_queue
+from silva.core.services.catalog import Cataloging, CatalogingTask
 
 
 class VersionedContentCataloging(Cataloging):
@@ -29,17 +29,20 @@
     def index(self, indexes=None, with_versions=True):
         super(VersionedContentCataloging, self).index(indexes=indexes)
         if with_versions:
+            task = CatalogingTask.get()
             for version in self.get_indexable_versions():
-                task_queue.index(version, indexes)
+                task.index(version, indexes)
 
     def reindex(self, indexes=None, with_versions=True):
         super(VersionedContentCataloging, self).reindex(indexes=indexes)
         if with_versions:
+            task = CatalogingTask.get()
             for version in self.get_indexable_versions():
-                task_queue.reindex(version, indexes)
+                task.reindex(version, indexes)
 
     def unindex(self, with_versions=True):
         if with_versions:
+            task = CatalogingTask.get()
             for version in self.get_indexable_versions():
-                task_queue.unindex(version)
+                task.unindex(version)
         super(VersionedContentCataloging, self).unindex()
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/testing.py
--- a/Products/Silva/testing.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/testing.py	Fri Aug 30 15:59:11 2013 +0200
@@ -12,7 +12,7 @@
 from Products.Silva import MAILHOST_ID
 from Products.Silva.EmailMessageService import email_queue
 from Products.Silva.tests.mockers import install_mockers
-from silva.core.services import task_queue
+from silva.core.services import CatalogingTask
 import Products.Silva
 
 from infrae.testbrowser.browser import Browser
@@ -34,9 +34,14 @@
     """Commit the code executed.
     """
 
+    def __init__(self, catalog=False):
+        self._catalog = catalog
+
     def __enter__(self):
         transaction.abort()
         transaction.begin()
+        if self._catalog:
+            CatalogingTask.get().activate()
 
     def __exit__(self, t, v, tb):
         if v is None and not transaction.isDoomed():
@@ -49,9 +54,8 @@
     """Commit the code executed, using a catalog queue
     """
 
-    def __enter__(self):
-        super(CatalogTransaction, self).__enter__()
-        task_queue.activate()
+    def __init__(self):
+        super(CatalogTransaction, self).__init__(catalog=True)
 
 
 class MockMail(object):
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/tests/test_file.py
--- a/Products/Silva/tests/test_file.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/tests/test_file.py	Fri Aug 30 15:59:11 2013 +0200
@@ -12,7 +12,7 @@
 
 from Products.Silva import File
 from Products.Silva.File.converters import have_command
-from Products.Silva.testing import FunctionalLayer, TestCase, tests
+from Products.Silva.testing import FunctionalLayer, TestCase, tests, Transaction
 from silva.core import interfaces
 from silva.core.services.interfaces import IMetadataService
 
@@ -35,21 +35,23 @@
     implementation = None
 
     def setUp(self):
-        self.root = self.layer.get_application()
+        with Transaction():
+            self.root = self.layer.get_application()
+            if self.implementation is not None:
+                self.root.service_files.storage = self.implementation
         self.layer.login('author')
 
     def create_test_file(self, filename='photo.tif'):
-        if self.implementation is not None:
-            self.root.service_files.storage = self.implementation
-        with self.layer.open_fixture(filename) as stream:
-            self.file_data = stream.read()
-            self.file_size = stream.tell()
-            stream.seek(0)
-            with tests.assertTriggersEvents(
-                'ObjectWillBeAddedEvent', 'ObjectAddedEvent',
-                'ContainerModifiedEvent', 'ObjectCreatedEvent'):
-                factory = self.root.manage_addProduct['Silva']
-                factory.manage_addFile(filename, 'Test File', stream)
+        with Transaction():
+            with self.layer.open_fixture(filename) as stream:
+                self.file_data = stream.read()
+                self.file_size = stream.tell()
+                stream.seek(0)
+                with tests.assertTriggersEvents(
+                    'ObjectWillBeAddedEvent', 'ObjectAddedEvent',
+                    'ContainerModifiedEvent', 'ObjectCreatedEvent'):
+                    factory = self.root.manage_addProduct['Silva']
+                    factory.manage_addFile(filename, 'Test File', stream)
         content =  self.root._getOb(filename)
         metadata = getUtility(IMetadataService).getMetadata(content)
         metadata.setValues('silva-extra', {
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/tests/test_folder.py
--- a/Products/Silva/tests/test_folder.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/tests/test_folder.py	Fri Aug 30 15:59:11 2013 +0200
@@ -5,7 +5,7 @@
 import unittest
 
 from Products.Silva.ftesting import public_settings
-from Products.Silva.testing import FunctionalLayer, tests
+from Products.Silva.testing import FunctionalLayer, tests, Transaction
 
 from silva.core.interfaces import IPublicationWorkflow, IMember
 from silva.core.interfaces import IFolder
@@ -18,8 +18,9 @@
     def setUp(self):
         self.root = self.layer.get_application()
         self.layer.login('editor')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addFolder('folder', 'Folder')
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFolder('folder', 'Folder')
 
     def test_implementation(self):
         self.assertTrue(verifyObject(IFolder, self.root.folder))
@@ -46,9 +47,10 @@
         current_datetime = self.root.folder.get_modification_datetime()
         self.assertNotEqual(current_datetime, None)
         self.assertNotEqual(self.root.get_modification_datetime(), None)
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addAutoTOC('index', 'Index')
-        self.assertNotEqual(
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addAutoTOC('index', 'Index')
+        self.assertGreater(
             self.root.folder.get_modification_datetime(),
             current_datetime)
 
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/tests/test_ghost.py
--- a/Products/Silva/tests/test_ghost.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/tests/test_ghost.py	Fri Aug 30 15:59:11 2013 +0200
@@ -3,13 +3,14 @@
 # See also LICENSE.txt
 
 import unittest
+import transaction
 
 from zope.interface.verify import verifyObject
 from zope.component import getUtility
 
 from Acquisition import aq_chain
 from DateTime import DateTime
-from Products.Silva.testing import FunctionalLayer
+from Products.Silva.testing import FunctionalLayer, Transaction
 from Products.Silva.ftesting import public_settings
 from Products.SilvaMetadata.interfaces import IMetadataService, ReadOnlyError
 
@@ -29,17 +30,21 @@
         self.root = self.layer.get_application()
         self.layer.login('editor')
 
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addMockupVersionedContent('document', 'Document')
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addMockupVersionedContent('document', 'Document')
+            factory.manage_addFolder('folder', 'Folder')
+            factory.manage_addImage('image', 'Image')
+
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addGhost('ghost', None)
+
         editable = self.root.document.get_editable()
         metadata = getUtility(IMetadataService).getMetadata(editable)
-        metadata.setValues('silva-extra', {
-                'modificationtime': DateTime('2011-04-25T12:00:00Z')})
-        factory.manage_addFolder('folder', 'Folder')
-        factory.manage_addImage('image', 'Image')
-
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addGhost('ghost', None)
+        metadata.setValues(
+            'silva-extra',
+            {'modificationtime': DateTime('2011-04-25T12:00:00Z')})
+        transaction.commit()
 
     def test_ghost(self):
         """Test simple ghost creation and life time.
@@ -336,34 +341,40 @@
         """Test that the ghost modification_time is the same than the
         document.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addGhost('ghost', None)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addGhost('ghost', None)
         ghost = self.root.ghost
         target = self.root.document
         self.assertEqual(ghost.get_modification_datetime(), None)
 
-        ghost.get_editable().set_haunted(target)
+        with Transaction():
+            ghost.get_editable().set_haunted(target)
         self.assertEqual(ghost.get_modification_datetime(), None)
 
-        IPublicationWorkflow(ghost).publish()
+        with Transaction():
+            IPublicationWorkflow(ghost).publish()
         self.assertEqual(
             ghost.get_modification_datetime(),
             target.get_modification_datetime())
 
-        IPublicationWorkflow(ghost).new_version()
-        ghost.get_editable().set_haunted(0)
+        with Transaction():
+            IPublicationWorkflow(ghost).new_version()
+            ghost.get_editable().set_haunted(0)
         self.assertEqual(       # We still see publlised version
             ghost.get_modification_datetime(),
             target.get_modification_datetime())
 
-        IPublicationWorkflow(ghost).publish()
+        with Transaction():
+            IPublicationWorkflow(ghost).publish()
         self.assertEqual(ghost.get_modification_datetime(), None)
 
     def test_head_request(self):
         """Test HEAD requests on Ghosts.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addGhost('ghost', None, haunted=self.root.document)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addGhost('ghost', None, haunted=self.root.document)
 
         with self.layer.get_browser() as browser:
             self.assertEqual(browser.open('/root/ghost', method='HEAD'), 200)
@@ -373,7 +384,9 @@
             # The ghost is broken, there is no modification date.
             self.assertNotIn('Last-Modified', browser.headers)
 
-        IPublicationWorkflow(self.root.ghost).publish()
+        with Transaction():
+            IPublicationWorkflow(self.root.ghost).publish()
+
         with self.layer.get_browser() as browser:
             self.assertEqual(browser.open('/root/ghost', method='HEAD'), 200)
             self.assertEqual(
@@ -388,14 +401,15 @@
     def test_render_protected_content(self):
         """Test rendering a protected content.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'Infrae', relative=False, url='http://infrae.com')
-        factory.manage_addGhost('ghost', None, haunted=self.root.link)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'Infrae', relative=False, url='http://infrae.com')
+            factory.manage_addGhost('ghost', None, haunted=self.root.link)
 
-        IAccessSecurity(self.root.link).minimum_role = 'Viewer'
-        IPublicationWorkflow(self.root.link).publish()
-        IPublicationWorkflow(self.root.ghost).publish()
+            IAccessSecurity(self.root.link).minimum_role = 'Viewer'
+            IPublicationWorkflow(self.root.link).publish()
+            IPublicationWorkflow(self.root.ghost).publish()
 
         with self.layer.get_browser(public_settings) as browser:
             browser.options.follow_redirect = False
@@ -417,13 +431,14 @@
         """Test rendering a ghost that points to a content that is
         closed.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'Infrae', relative=False, url='http://infrae.com')
-        factory.manage_addGhost('ghost', None, haunted=self.root.link)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'Infrae', relative=False, url='http://infrae.com')
+            factory.manage_addGhost('ghost', None, haunted=self.root.link)
 
-        IPublicationWorkflow(self.root.link).publish()
-        IPublicationWorkflow(self.root.ghost).publish()
+            IPublicationWorkflow(self.root.link).publish()
+            IPublicationWorkflow(self.root.ghost).publish()
 
         with self.layer.get_browser(public_settings) as browser:
             browser.options.follow_redirect = False
@@ -434,7 +449,8 @@
                 browser.headers['Location'],
                 'http://infrae.com')
 
-        IPublicationWorkflow(self.root.link).close()
+        with Transaction():
+            IPublicationWorkflow(self.root.link).close()
         with self.layer.get_browser(public_settings) as browser:
             browser.options.follow_redirect = False
             self.assertEqual(
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/tests/test_image.py
--- a/Products/Silva/tests/test_image.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/tests/test_image.py	Fri Aug 30 15:59:11 2013 +0200
@@ -19,8 +19,8 @@
 from silva.core.services.interfaces import IMetadataService
 
 from Products.Silva import File
-from Products.Silva.testing import FunctionalLayer, TestCase
-from Products.Silva.testing import assertTriggersEvents, CatalogTransaction
+from Products.Silva.testing import FunctionalLayer, TestCase, Transaction
+from Products.Silva.testing import assertTriggersEvents
 
 
 def search(**query):
@@ -38,16 +38,18 @@
         self.root = self.layer.get_application()
         self.layer.login('author')
 
-        if self.implementation is not None:
-            self.root.service_files.storage = self.implementation
+        with Transaction():
+            if self.implementation is not None:
+                self.root.service_files.storage = self.implementation
 
-        factory = self.root.manage_addProduct['Silva']
-        with self.layer.open_fixture('photo.tif') as image:
-            self.image_data = image.read()
-            self.image_size = image.tell()
-            image.seek(0, 0)
+            factory = self.root.manage_addProduct['Silva']
+            with self.layer.open_fixture('photo.tif') as image:
+                self.image_data = image.read()
+                self.image_size = image.tell()
+                image.seek(0, 0)
 
-            factory.manage_addImage('test_image', u'Image élaboré', image)
+                factory.manage_addImage('test_image', u'Image élaboré', image)
+
         image = self.root._getOb('test_image')
         metadata = getUtility(IMetadataService).getMetadata(image)
         metadata.setValues('silva-extra', {
@@ -56,59 +58,63 @@
     def test_image(self):
         """Test image content.
         """
-        content = self.root.test_image
-        self.assertTrue(verifyObject(interfaces.IAsset, content))
-        self.assertTrue(verifyObject(interfaces.IImage, content))
+        image = self.root.test_image
+        self.assertTrue(verifyObject(interfaces.IAsset, image))
+        self.assertTrue(verifyObject(interfaces.IImage, image))
+        self.assertNotEqual(image.get_modification_datetime(), None)
+        self.assertNotEqual(image.get_creation_datetime(), None)
 
         # Asset methods
-        self.assertEquals(content.get_content_type(), 'image/tiff')
-        self.assertEquals(content.get_file_size(), self.image_size)
-        self.assertEquals(content.get_filename(), 'test_image.tiff')
-        self.assertEquals(content.get_mime_type(), 'image/tiff')
+        self.assertEquals(image.get_content_type(), 'image/tiff')
+        self.assertEquals(image.get_file_size(), self.image_size)
+        self.assertEquals(image.get_filename(), 'test_image.tiff')
+        self.assertEquals(image.get_mime_type(), 'image/tiff')
 
         # Image methods
-        self.assertEquals(content.get_format(), 'TIFF')
-        self.assertEquals(content.get_web_format(), 'JPEG')
-        self.assertEquals(content.get_dimensions(), (960, 1280))
-        self.assertEquals(str(content.get_orientation()), "portrait")
-        content.set_web_presentation_properties('JPEG', '100x100', '')
+        self.assertEquals(image.get_format(), 'TIFF')
+        self.assertEquals(image.get_web_format(), 'JPEG')
+        self.assertEquals(image.get_dimensions(), (960, 1280))
+        self.assertEquals(str(image.get_orientation()), "portrait")
+        image.set_web_presentation_properties('JPEG', '100x100', '')
         with self.assertRaises(ValueError):
-            content.get_image(hires=False, webformat=False)
-        self.assertTrue(content.tag() is not None)
-        self.assertEquals(content.get_web_format(), 'JPEG')
+            image.get_image(hires=False, webformat=False)
+        self.assertTrue(image.tag() is not None)
+        self.assertEquals(image.get_web_format(), 'JPEG')
 
-        data = io.BytesIO(content.get_image(hires=False, webformat=True))
+        data = io.BytesIO(image.get_image(hires=False, webformat=True))
         pil_image = PILImage.open(data)
         self.assertEquals((100, 100), pil_image.size)
         self.assertEquals('JPEG', pil_image.format)
 
-        data = content.get_image(hires=True, webformat=False)
+        data = image.get_image(hires=True, webformat=False)
         self.assertHashEqual(self.image_data, data)
 
-        data = io.BytesIO(content.get_image(hires=True, webformat=True))
+        data = io.BytesIO(image.get_image(hires=True, webformat=True))
         pil_image = PILImage.open(data)
         self.assertEquals((960, 1280), pil_image.size)
         self.assertEquals('JPEG', pil_image.format)
 
     def test_upload_image_with_existing_id(self):
-        factory = self.root.manage_addProduct['Silva']
+        with Transaction():
+            with self.layer.open_fixture('photo.tif') as image:
+                factory = self.root.manage_addProduct['Silva']
+                factory.manage_addImage('test_image_id', 'Test Image 1', image)
+                with self.assertRaises(ValueError) as error:
+                    factory.manage_addImage('test_image_id', 'Test Image 2', image)
 
-        with self.layer.open_fixture('photo.tif') as image:
-            factory.manage_addImage('test_image_id', 'Test Image 1', image)
-            with self.assertRaises(ValueError) as error:
-                factory.manage_addImage('test_image_id', 'Test Image 2', image)
-
-        self.assertEqual(str(error.exception), "Please provide a unique id: ${reason}")
+        self.assertEqual(
+            str(error.exception),
+            "Please provide a unique id: ${reason}")
 
     def test_rename_image(self):
         """Move an image and check that the filename is updated correctly.
         """
-        content = getattr(self.root, 'test_image')
-        self.assertEquals(content.get_filename(), 'test_image.tiff')
+        image = self.root._getOb('test_image')
+        self.assertEquals(image.get_filename(), 'test_image.tiff')
         self.root.manage_renameObjects(['test_image'], ['new_image.gif'])
 
-        content = getattr(self.root, 'new_image.gif')
-        self.assertEquals(content.get_filename(), 'new_image.tiff')
+        image = self.root._getOb('new_image.gif')
+        self.assertEquals(image.get_filename(), 'new_image.tiff')
 
     def test_copy_paste_image(self):
         """Cut and paste an image. Check the filename is updated.
@@ -125,12 +131,13 @@
     def test_catalog(self):
         """Verify that the image is properly catalogued.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addFolder('folder', 'Folder')
+        with Transaction(catalog=False):
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFolder('folder', 'Folder')
 
-        factory = self.root.folder.manage_addProduct['Silva']
-        with self.layer.open_fixture('photo.tif') as image:
-            factory.manage_addImage('image', 'Test Image', image)
+            factory = self.root.folder.manage_addProduct['Silva']
+            with self.layer.open_fixture('photo.tif') as image:
+                factory.manage_addImage('image', 'Test Image', image)
 
         # Test that the image is catalogued (and not the sub-files)
         self.assertItemsEqual(
@@ -141,11 +148,10 @@
     def test_catalog_transaction(self):
         """Verify that the image is properly catalogued.
         """
-        with CatalogTransaction():
+        with Transaction(catalog=True):
             factory = self.root.manage_addProduct['Silva']
             factory.manage_addFolder('folder', 'Folder')
 
-        with CatalogTransaction():
             factory = self.root.folder.manage_addProduct['Silva']
             with self.layer.open_fixture('photo.tif') as image:
                 factory.manage_addImage('image', 'Test Image', image)
@@ -443,9 +449,10 @@
     def test_empty_image(self):
         """Test an image that doesn't store an image.
         """
-        factory = self.root.manage_addProduct['Silva']
-        with assertTriggersEvents('ObjectCreatedEvent'):
-            factory.manage_addImage('image', 'Image')
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            with assertTriggersEvents('ObjectCreatedEvent'):
+                factory.manage_addImage('image', 'Image')
 
         image = self.root.image
         self.assertTrue(verifyObject(interfaces.IImage, image))
@@ -455,6 +462,8 @@
         self.assertEqual(image.get_file_size(), 0)
         self.assertEqual(image.get_file_system_path(), None)
         self.assertEqual(image.get_image(), None)
+        self.assertNotEqual(image.get_modification_datetime(), None)
+        self.assertNotEqual(image.get_creation_datetime(), None)
 
         payload = interfaces.IAssetPayload(image)
         self.assertTrue(verifyObject(interfaces.IAssetPayload, payload))
@@ -477,9 +486,11 @@
     def test_get_crop_box(self):
         """Test get_crop_box method that either return or parse a crop_box.
         """
-        factory = self.root.manage_addProduct['Silva']
-        with self.layer.open_fixture('photo.tif') as image:
-            factory.manage_addImage('image', 'Torvald', image)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            with self.layer.open_fixture('photo.tif') as image:
+                factory.manage_addImage('image', 'Torvald', image)
+
         self.assertEqual(
             self.root.image.get_crop_box(crop="242x379-392x479"),
             (242, 379, 392, 479))
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/tests/test_versioning_metadata.py
--- a/Products/Silva/tests/test_versioning_metadata.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/tests/test_versioning_metadata.py	Fri Aug 30 15:59:11 2013 +0200
@@ -7,7 +7,7 @@
 from zope.component import getUtility
 from zope.interface.verify import verifyObject
 
-from Products.Silva.testing import FunctionalLayer
+from Products.Silva.testing import FunctionalLayer, Transaction
 from Products.SilvaMetadata.interfaces import ReadOnlyError
 from silva.core.services.interfaces import IMetadataService
 from silva.core.interfaces import IMember, IContainerManager, IVersionedContent
@@ -17,10 +17,11 @@
     layer = FunctionalLayer
 
     def setUp(self):
-        self.root = self.layer.get_application()
-        self.layer.login('editor')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addMockupVersionedContent('document', 'Document')
+        with Transaction():
+            self.root = self.layer.get_application()
+            self.layer.login('editor')
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addMockupVersionedContent('document', 'Document')
 
     def test_created_information(self):
         """Test that default information is properly filled on a
@@ -53,9 +54,11 @@
         filled. Although the document have been created by editor, the
         copy have been created by author.
         """
-        self.layer.login('author')
-        with IContainerManager(self.root).copier() as copier:
-            copy = copier(self.root.document)
+        with Transaction():
+            self.layer.login('author')
+            with IContainerManager(self.root).copier() as copier:
+                copy = copier(self.root.document)
+
         self.assertTrue(verifyObject(IVersionedContent, copy))
 
         creator = copy.get_creator_info()
@@ -72,10 +75,11 @@
     layer = FunctionalLayer
 
     def setUp(self):
-        self.root = self.layer.get_application()
-        self.layer.login('editor')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addMockupVersionedContent('document', 'Document')
+        with Transaction():
+            self.root = self.layer.get_application()
+            self.layer.login('editor')
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addMockupVersionedContent('document', 'Document')
 
     def test_version_metadata(self):
         """Test simple validation on the metadata system while setting
diff -r 7c695cc950df -r caf1cc5b1bd4 Products/Silva/tests/test_xml_export.py
--- a/Products/Silva/tests/test_xml_export.py	Wed Aug 28 12:00:22 2013 +0200
+++ b/Products/Silva/tests/test_xml_export.py	Fri Aug 30 15:59:11 2013 +0200
@@ -7,11 +7,13 @@
 import re
 import io
 import unittest
+import transaction
 
 from zope.component import getAdapter, getUtility
 
 # Silva
 from Products.Silva.testing import FunctionalLayer, TestCase, TestRequest
+from Products.Silva.testing import Transaction
 
 from silva.core import interfaces
 from silva.core.interfaces.errors import ExternalReferenceError
@@ -51,6 +53,7 @@
         return DATETIME_RE.sub(r'YYYY-MM-DDTHH:MM:SS', string)
 
     def assertExportFail(self, content, error=ExternalReferenceError, options={}):
+        #transaction.commit()
         exporter = Exporter(content, TestRequest(), options.copy())
         with self.assertRaises(error):
             exporter.getString()
@@ -60,6 +63,7 @@
         """Verify that the xml result of an export is the same than
         the one contained in a test file.
         """
+        #transaction.commit()
         exporter = Exporter(content, TestRequest(), options.copy())
         with self.layer.open_fixture(filename) as xml_file:
             expected_xml = xml_file.read().format(
@@ -75,19 +79,21 @@
     """
 
     def setUp(self):
-        super(XMLExportTestCase, self).setUp()
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addFolder(
-            'folder', 'This is <boo>a</boo> folder',
-            policy_name='Silva AutoTOC')
+        with Transaction():
+            super(XMLExportTestCase, self).setUp()
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFolder(
+                'folder', 'This is <boo>a</boo> folder',
+                policy_name='Silva AutoTOC')
 
     def test_folder_autotoc_index(self):
         """Export a folder.
         """
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addFolder('folder', 'This is &another; a subfolder')
-        factory = self.root.folder.folder.manage_addProduct['Silva']
-        factory.manage_addAutoTOC('index', 'This is &another; a subfolder')
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addFolder('folder', 'This is &another; a subfolder')
+            factory = self.root.folder.folder.manage_addProduct['Silva']
+            factory.manage_addAutoTOC('index', 'This is &another; a subfolder')
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -99,8 +105,9 @@
         """Test the fallback exporter: create a Zope 2 folder in a
         Silva folder and export it.
         """
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addMockupVersionedContent('mockup', 'Mockup Content')
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addMockupVersionedContent('mockup', 'Mockup Content')
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -118,15 +125,16 @@
     def test_indexer(self):
         """Export an indexer.
         """
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addIndexer('indexer', 'Index of this site')
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addIndexer('indexer', 'Index of this site')
 
-        metadata = getUtility(IMetadataService).getMetadata(
-            self.root.folder.indexer)
-        metadata.setValues(
-            'silva-extra',
-            {'content_description': 'Index the content of your website.',
-             'comment': 'Nothing special is required.'})
+            metadata = getUtility(IMetadataService).getMetadata(
+                self.root.folder.indexer)
+            metadata.setValues(
+                'silva-extra',
+                {'content_description': 'Index the content of your website.',
+                 'comment': 'Nothing special is required.'})
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -138,11 +146,12 @@
     def test_ghost(self):
         """Export a ghost.
         """
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'New website', url='http://infrae.com/', relative=False)
-        factory.manage_addGhost(
-            'ghost', None, haunted=self.root.folder.link)
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'New website', url='http://infrae.com/', relative=False)
+            factory.manage_addGhost(
+                'ghost', None, haunted=self.root.folder.link)
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -155,12 +164,13 @@
         """Export a ghost that link something outside of export
         tree. It should error by default.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'New website', url='http://infrae.com/', relative=False)
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addGhost(
-            'ghost', None, haunted=self.root.link)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'New website', url='http://infrae.com/', relative=False)
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addGhost(
+                'ghost', None, haunted=self.root.link)
 
         self.assertExportFail(self.root.folder)
 
@@ -168,13 +178,14 @@
         """Export a ghost that link something outside of export
         tree with the option external_references set to True.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'New website', url='http://infrae.com/', relative=False)
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addGhost(
-            'ghost', None, haunted=self.root.link)
-        version = self.root.folder.ghost.get_editable()
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'New website', url='http://infrae.com/', relative=False)
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addGhost(
+                'ghost', None, haunted=self.root.link)
+            version = self.root.folder.ghost.get_editable()
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -193,20 +204,20 @@
     def test_ghost_folder(self):
         """Export a ghost folder.
         """
-        self.layer.login('chiefeditor')
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addFolder('container', 'Content')
-        factory.manage_addGhostFolder(
-            'ghost', None, haunted=self.root.folder.container)
-        factory = self.root.folder.container.manage_addProduct['Silva']
-        factory.manage_addAutoTOC('index', 'Content')
-        factory.manage_addLink(
-            'link', 'Infrae', url='http://infrae.com', relative=False)
-        factory.manage_addFile('file', 'Torvald blob')
+        with Transaction():
+            self.layer.login('chiefeditor')
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addFolder('container', 'Content')
+            factory.manage_addGhostFolder(
+                'ghost', None, haunted=self.root.folder.container)
+            factory = self.root.folder.container.manage_addProduct['Silva']
+            factory.manage_addAutoTOC('index', 'Content')
+            factory.manage_addLink(
+                'link', 'Infrae', url='http://infrae.com', relative=False)
+            factory.manage_addFile('file', 'Torvald blob')
+            self.root.folder.ghost.haunt()
 
-        self.root.folder.ghost.haunt()
         self.layer.login('author')
-
         exporter = self.assertExportEqual(
             self.root.folder,
             'test_export_ghostfolder.silvaxml')
@@ -223,31 +234,33 @@
     def test_ghost_folder_external_reference(self):
         """Export a ghost folder but not the ghosted folder.
         """
-        self.layer.login('chiefeditor')
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addFolder('container', 'Content')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addGhostFolder(
-            'ghost', None, haunted=self.root.folder.container)
-        factory = self.root.folder.container.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'Infrae', url='http://infrae.com', relative=False)
-        factory.manage_addFile('file', 'Torvald blob')
+        with Transaction():
+            self.layer.login('chiefeditor')
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addFolder('container', 'Content')
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addGhostFolder(
+                'ghost', None, haunted=self.root.folder.container)
+            factory = self.root.folder.container.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'Infrae', url='http://infrae.com', relative=False)
+            factory.manage_addFile('file', 'Torvald blob')
+            self.root.ghost.haunt()
 
-        self.root.ghost.haunt()
         self.layer.login('author')
         self.assertExportFail(self.root.ghost)
 
     def test_link_relative(self):
         """Export a link with to an another Silva object.
         """
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addFile('file', 'Torvald file')
-        factory.manage_addFolder('new', 'New changes')
-        factory = self.root.folder.new.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'Last file',
-            relative=True, target=self.root.folder.file)
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addFile('file', 'Torvald file')
+            factory.manage_addFolder('new', 'New changes')
+            factory = self.root.folder.new.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'Last file',
+                relative=True, target=self.root.folder.file)
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -265,20 +278,22 @@
     def test_link_relative_external_reference(self):
         """Export a link with to an another Silva object.
         """
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addFile('file', 'Torvald file')
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'Last file', relative=True, target=self.root.file)
+        with Transaction():
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFile('file', 'Torvald file')
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'Last file', relative=True, target=self.root.file)
 
         self.assertExportFail(self.root.folder)
 
     def test_missing_references(self):
         """Test export of missing references.
         """
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addLink('link', 'Broken Link', relative=True)
-        factory.manage_addGhost('ghost', None)
+        with Transaction():
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addLink('link', 'Broken Link', relative=True)
+            factory.manage_addGhost('ghost', None)
 
         exporter = self.assertExportEqual(
             self.root.folder, 'test_export_broken_references.silvaxml')
@@ -292,24 +307,25 @@
     """
 
     def setUp(self):
-        super(XMLExportVersionsTestCase, self).setUp()
-        self.layer.login('editor')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addFolder('folder', 'Export Folder')
-        factory = self.root.folder.manage_addProduct['Silva']
-        factory.manage_addFile('file', 'Torvald file')
-        factory.manage_addFolder('new', 'New changes')
-        factory = self.root.folder.new.manage_addProduct['Silva']
-        factory.manage_addLink(
-            'link', 'Initial file',
-            relative=True, target=self.root.folder.file)
-        link = self.root.folder.new.link
-        IPublicationWorkflow(link).publish()
-        IPublicationWorkflow(link).new_version()
-        link.get_editable().set_title('Updated file')
-        IPublicationWorkflow(link).publish()
-        IPublicationWorkflow(link).new_version()
-        link.get_editable().set_title('Final version of the file')
+        with Transaction():
+            super(XMLExportVersionsTestCase, self).setUp()
+            self.layer.login('editor')
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFolder('folder', 'Export Folder')
+            factory = self.root.folder.manage_addProduct['Silva']
+            factory.manage_addFile('file', 'Torvald file')
+            factory.manage_addFolder('new', 'New changes')
+            factory = self.root.folder.new.manage_addProduct['Silva']
+            factory.manage_addLink(
+                'link', 'Initial file',
+                relative=True, target=self.root.folder.file)
+            link = self.root.folder.new.link
+            IPublicationWorkflow(link).publish()
+            IPublicationWorkflow(link).new_version()
+            link.get_editable().set_title('Updated file')
+            IPublicationWorkflow(link).publish()
+            IPublicationWorkflow(link).new_version()
+            link.get_editable().set_title('Final version of the file')
 
     def test_link_editable_only_previewable(self):
         """Export only the previewable version of a a link that have
@@ -351,9 +367,10 @@
         """Export only the viewable version of a link that have
         multiple versions available, but none published.
         """
-        link = self.root.folder.new.link
-        IPublicationWorkflow(link).publish()
-        IPublicationWorkflow(link).close()
+        with Transaction():
+            link = self.root.folder.new.link
+            IPublicationWorkflow(link).publish()
+            IPublicationWorkflow(link).close()
 
         # XXX This will trigger a bug on import. We need to find a
         # solution that doesn't imply to change all importers again.
@@ -375,9 +392,10 @@
         """Export only the previewable version of a link that have
         multiple versions available, but none published.
         """
-        link = self.root.folder.new.link
-        IPublicationWorkflow(link).publish()
-        IPublicationWorkflow(link).close()
+        with Transaction():
+            link = self.root.folder.new.link
+            IPublicationWorkflow(link).publish()
+            IPublicationWorkflow(link).close()
 
         exporter = self.assertExportEqual(
             self.root.folder,
@@ -417,17 +435,19 @@
     layer = FunctionalLayer
 
     def setUp(self):
-        self.root = self.layer.get_application()
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addFolder('folder', 'Folder')
+        with Transaction():
+            self.root = self.layer.get_application()
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFolder('folder', 'Folder')
 
     def test_zip_export(self):
         """Import/export a Zip file.
         """
         # XXX This test needs improvement.
-        with self.layer.open_fixture('test1.zip') as zip_import:
-            importer = interfaces.IArchiveFileImporter(self.root.folder)
-            succeeded, failed = importer.importArchive(zip_import)
+        with Transaction():
+            with self.layer.open_fixture('test1.zip') as zip_import:
+                importer = interfaces.IArchiveFileImporter(self.root.folder)
+                succeeded, failed = importer.importArchive(zip_import)
 
         self.assertItemsEqual(
             succeeded,