[Products.Silva][Sylvain Viollon] Update ghost folder code to su...

[email protected] Thu, 14 Nov 2013 15:07:35 +0100
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Thu Nov 14 15:07:31 2013 +0100
revision:  11882:3719ea761135 in Products.Silva
branch:    2.4
details:   https://hg.infrae.com/Products.Silva?cmd=changeset;node=3719ea761135
modified:  Products/Silva/Folder/management.py Products/Silva/Ghost/content.py Products/Silva/GhostAsset/content.py Products/Silva/GhostFolder/content.py Products/Silva/tests/test_folder_ghoster.py Products/Silva/tests/test_ghost_folder.py
added:     
removed:   
log:       Update ghost folder code to support invalid content.


diffstat:

 Products/Silva/Folder/management.py         |   7 +++-
 Products/Silva/Ghost/content.py             |   7 +++-
 Products/Silva/GhostAsset/content.py        |   7 +++-
 Products/Silva/GhostFolder/content.py       |  24 +++++++++++---
 Products/Silva/tests/test_folder_ghoster.py |  35 +++++++++++++++++++++++
 Products/Silva/tests/test_ghost_folder.py   |  44 +++++++++++++++++++++-------
 6 files changed, 100 insertions(+), 24 deletions(-)

diffs (313 lines):

diff -r d237fdfdfed4 -r 3719ea761135 Products/Silva/Folder/management.py
--- a/Products/Silva/Folder/management.py	Tue Nov 12 17:19:36 2013 +0100
+++ b/Products/Silva/Folder/management.py	Thu Nov 14 15:07:31 2013 +0100
@@ -26,7 +26,7 @@
 from silva.core import conf as silvaconf
 from silva.core.interfaces import IContainerManager
 from silva.core.interfaces import IAddableContents
-from silva.core.interfaces import IContainer, IAsset
+from silva.core.interfaces import IContainer
 from silva.core.interfaces import ContainerError, ContentError
 from silva.core.interfaces import ISilvaNameChooser
 from silva.translations import translate as _
@@ -239,7 +239,10 @@
                     result = self.__copy(content, identifier)
                 else:
                     identifier = self.__make_id('ghost', content.getId())
-                    result = factory(identifier)
+                    try:
+                        result = factory(identifier)
+                    except ContentError as result:
+                        pass
 
             content = yield result
 
diff -r d237fdfdfed4 -r 3719ea761135 Products/Silva/Ghost/content.py
--- a/Products/Silva/Ghost/content.py	Tue Nov 12 17:19:36 2013 +0100
+++ b/Products/Silva/Ghost/content.py	Thu Nov 14 15:07:31 2013 +0100
@@ -17,7 +17,7 @@
 from Products.Silva import SilvaPermissions
 
 from silva.core import conf as silvaconf
-from silva.core.interfaces.errors import ContentInvalidTarget
+from silva.core.interfaces.errors import ContentInvalidTarget, ContentError
 from silva.core.interfaces import IContent, IGhost, IGhostVersion
 from silva.core.interfaces import IPublicationWorkflow, IGhostManager
 from silva.translations import translate as _
@@ -103,7 +103,10 @@
         assert self.manager.ghost is None
         ghost = None
         factory = self.manager.container.manage_addProduct['Silva']
-        factory.manage_addGhost(self.identifier, None)
+        try:
+            factory.manage_addGhost(self.identifier, None)
+        except ValueError as error:
+            raise ContentError(error[0], content=self.target)
         ghost = self.manager.container._getOb(self.identifier)
         version = ghost.get_editable()
         version.set_haunted(
diff -r d237fdfdfed4 -r 3719ea761135 Products/Silva/GhostAsset/content.py
--- a/Products/Silva/GhostAsset/content.py	Tue Nov 12 17:19:36 2013 +0100
+++ b/Products/Silva/GhostAsset/content.py	Thu Nov 14 15:07:31 2013 +0100
@@ -23,7 +23,7 @@
 from silva.core.interfaces import IAssetPayload, IGhostManager
 from silva.core.interfaces import IGhostAsset, IAsset
 from silva.core.interfaces import IImage, IImageIncluable
-from silva.core.interfaces.errors import AssetInvalidTarget
+from silva.core.interfaces.errors import AssetInvalidTarget, ContentError
 from silva.core.references.reference import DeleteSourceReferenceValue
 from silva.core.references.reference import get_content_from_id, get_content_id
 from silva.core.views.interfaces import IContentURL
@@ -126,7 +126,10 @@
     def create(self, recursive=False):
         assert self.manager.ghost is None
         factory = self.manager.container.manage_addProduct['Silva']
-        factory.manage_addGhostAsset(self.identifier, None)
+        try:
+            factory.manage_addGhostAsset(self.identifier, None)
+        except ValueError as error:
+            raise ContentError(error[0], content=self.target)
         ghost = self.manager.container._getOb(self.identifier)
         ghost.set_haunted(self.target, auto_delete=self.manager.auto_delete)
         self.manager.ghost = ghost
diff -r d237fdfdfed4 -r 3719ea761135 Products/Silva/GhostFolder/content.py
--- a/Products/Silva/GhostFolder/content.py	Tue Nov 12 17:19:36 2013 +0100
+++ b/Products/Silva/GhostFolder/content.py	Thu Nov 14 15:07:31 2013 +0100
@@ -27,6 +27,7 @@
 from silva.core.interfaces import IContainer, IGhostFolder, IGhostManager
 from silva.core.interfaces import IPublication
 from silva.core.interfaces.errors import ContainerInvalidTarget
+from silva.core.interfaces.errors import ContentError, ContentErrorBundle
 from silva.translations import translate as _
 
 
@@ -89,6 +90,7 @@
         if haunted is None:
             return False
         stack = self._haunt_diff(haunted, self)
+        errors = []
 
         while stack:
             # breadth first search
@@ -105,15 +107,22 @@
             if g_id is not None:
                 g_ob = g_container._getOb(g_id)
 
-            g_ob = get_factory(h_ob)(
-                ghost=g_ob,
-                container=g_container,
-                auto_delete=True,
-                auto_publish=True).modify(h_ob, h_id).verify()
+            try:
+                g_ob = get_factory(h_ob)(
+                    ghost=g_ob,
+                    container=g_container,
+                    auto_delete=True,
+                    auto_publish=True).modify(h_ob, h_id).verify()
+            except ContentError as error:
+                errors.append(error)
 
             if IContainer.providedBy(h_ob) and g_ob is not None:
                 stack.extend(self._haunt_diff(h_ob, g_ob))
 
+        if errors:
+            raise ContentErrorBundle(
+                _(u"Error while synchronizing the Ghost Folder"),
+                content=self, errors=errors)
         return True
 
     def _haunt_diff(self, haunted, ghost):
@@ -216,7 +225,10 @@
     def create(self, recursive=False):
         assert self.manager.ghost is None
         factory = self.manager.container.manage_addProduct['Silva']
-        factory.manage_addGhostFolder(self.identifier, None)
+        try:
+            factory.manage_addGhostFolder(self.identifier, None)
+        except ValueError as error:
+            raise ContentError(error[0], content=self.target)
         ghost = self.manager.container._getOb(self.identifier)
         ghost.set_haunted(self.target, auto_delete=self.manager.auto_delete)
         if recursive:
diff -r d237fdfdfed4 -r 3719ea761135 Products/Silva/tests/test_folder_ghoster.py
--- a/Products/Silva/tests/test_folder_ghoster.py	Tue Nov 12 17:19:36 2013 +0100
+++ b/Products/Silva/tests/test_folder_ghoster.py	Thu Nov 14 15:07:31 2013 +0100
@@ -6,10 +6,12 @@
 
 from silva.core.interfaces import IContainerManager, IPublicationWorkflow
 from silva.core.interfaces import IGhost, IGhostVersion, IGhostAsset, IGhostFolder
+from silva.core.interfaces.errors import IContentErrorBundle
 from zope.interface.verify import verifyObject
 
 from Products.Silva.testing import assertTriggersEvents
 from Products.Silva.testing import FunctionalLayer, Transaction
+from Products.Silva.tests.mockers import IMockupNonPublishable
 
 
 class EditorFolderGhosterTestCase(unittest.TestCase):
@@ -33,6 +35,39 @@
 
             IPublicationWorkflow(self.root.source.data).publish()
 
+    def test_invalid(self):
+        """Pasting a content as a ghost folder that contains a content
+        with an invalid identifier.
+        """
+        self.root.source.manage_renameObject('folder', 'index')
+        manager = IContainerManager(self.root.target)
+        with assertTriggersEvents('ObjectWillBeAddedEvent',
+                                  'ObjectAddedEvent',
+                                  'ContainerModifiedEvent'):
+            with manager.ghoster() as ghoster:
+                error = ghoster(self.root.source)
+
+        self.assertTrue(verifyObject(IContentErrorBundle, error))
+        self.assertEqual(error.content, self.root.target.source)
+        self.assertEqual(error.reason, u'Error while synchronizing the Ghost Folder')
+        self.assertEqual(len(error.errors), 1)
+
+    def test_other(self):
+        """Pasting a content that doesn't have a ghost
+        implementation. It should be copied.
+        """
+        factory = self.root.source.manage_addProduct['Silva']
+        factory.manage_addMockupNonPublishable('stuff', 'Stuff')
+        manager = IContainerManager(self.root.target)
+        with assertTriggersEvents('ObjectWillBeAddedEvent',
+                                  'ObjectAddedEvent',
+                                  'ContainerModifiedEvent'):
+            with manager.ghoster() as ghoster:
+                ghost = ghoster(self.root.source.stuff)
+
+        self.assertTrue(verifyObject(IMockupNonPublishable, ghost))
+        self.assertIn('stuff', self.root.target.objectIds())
+
     def test_asset(self):
         """Pasting an asset as a ghost asset.
         """
diff -r d237fdfdfed4 -r 3719ea761135 Products/Silva/tests/test_ghost_folder.py
--- a/Products/Silva/tests/test_ghost_folder.py	Tue Nov 12 17:19:36 2013 +0100
+++ b/Products/Silva/tests/test_ghost_folder.py	Thu Nov 14 15:07:31 2013 +0100
@@ -15,7 +15,8 @@
 from silva.core.interfaces import IContainerManager, IPublicationWorkflow
 from silva.core.interfaces import IGhost, IGhostAsset, IGhostVersion
 from silva.core.interfaces import IPublication, IFolder, IGhostFolder
-from silva.core.interfaces import errors
+from silva.core.interfaces.errors import EmptyInvalidTarget, CircularInvalidTarget
+from silva.core.interfaces.errors import ContentError, ContainerInvalidTarget
 from silva.core.references.interfaces import IReferenceService, IReferenceValue
 
 
@@ -46,7 +47,7 @@
         factory.manage_addMockupVersionedContent('document', 'Document')
         factory.manage_addFolder('folder', 'Folder')
 
-    def test_get_folder(self):
+    def test_ghost_folder(self):
         """Test a Ghost Folder haunting to a Folder.
         """
         factory = self.root.target.manage_addProduct['Silva']
@@ -183,7 +184,7 @@
             aq_chain(reference.source),
             aq_chain(ghost))
 
-    def test_ghost_to_publication(self):
+    def test_convert_to_publication(self):
         """Test convertion of a Ghost Folder to a Publication.
         """
         factory = self.root.target.manage_addProduct['Silva']
@@ -222,7 +223,7 @@
         self.assertTrue(verifyObject(IGhost, ghost.index))
         self.assertTrue(verifyObject(IGhost, ghost.document))
 
-    def test_ghost_to_folder(self):
+    def test_convert_to_folder(self):
         """Test Ghost Folder convertion to a regular Folder.
         """
         factory = self.root.target.manage_addProduct['Silva']
@@ -342,22 +343,22 @@
         ghost = self.root.target.ghost
         self.assertEqual(
             ghost.get_link_status(),
-            errors.EmptyInvalidTarget())
+            EmptyInvalidTarget())
 
         ghost.set_haunted(self.root.folder.document)
         self.assertEqual(
             ghost.get_link_status(),
-            errors.ContainerInvalidTarget())
+            ContainerInvalidTarget())
 
         ghost.set_haunted(self.root)
         self.assertEqual(
             ghost.get_link_status(),
-            errors.CircularInvalidTarget())
+            CircularInvalidTarget())
 
         ghost.set_haunted(self.root.target.ghost)
         self.assertEqual(
             ghost.get_link_status(),
-            errors.CircularInvalidTarget())
+            CircularInvalidTarget())
 
         ghost.set_haunted(self.root.target.folder)
         self.assertEqual(ghost.get_link_status(), None)
@@ -367,12 +368,12 @@
         ghost.set_haunted(self.root.target.ghost.folder)
         self.assertEqual(
             ghost.get_link_status(),
-            errors.CircularInvalidTarget())
+            CircularInvalidTarget())
 
         ghost.set_haunted(0)
         self.assertEqual(
             ghost.get_link_status(),
-            errors.EmptyInvalidTarget())
+            EmptyInvalidTarget())
 
     def test_ghost_title(self):
         """Test Ghost Folder title.
@@ -434,6 +435,25 @@
         self.assertFalse('folder' in ghost.objectIds())
         self.assertTrue('publication' in ghost.objectIds())
 
+    def test_ghost_haunt_errors(self):
+        """Test modifications: a ghost folder is haunted a folder
+        containing content with invalid identifiers.
+        """
+        factory = self.root.target.manage_addProduct['Silva']
+        factory.manage_addGhostFolder('ghost', None, haunted=self.root.folder)
+
+        ghost = self.root.target.ghost
+        self.root.folder.folder.manage_renameObject('asset', 'cb_asset')
+        with self.assertRaises(ContentError):
+            ghost.haunt()
+
+        self.assertItemsEqual(
+            ghost.objectIds(),
+            ['index', 'document', 'folder', 'publication'])
+        self.assertItemsEqual(
+            ghost.folder.objectIds(),
+            ['document'])
+
     def test_ghost_haunt_add_ghosts(self):
         """Test modifications: adding content in the target creates
         new ghosts in the ghost folder when the ghost folder is
@@ -801,7 +821,7 @@
         self.assertTrue('backup' in self.root.folder.objectIds())
         self.assertEqual(
             self.root.folder.backup.get_link_status(),
-            errors.EmptyInvalidTarget())
+            EmptyInvalidTarget())
         self.assertFalse('backup' in ghost.objectIds())
 
         # Ghosting should not change anything
@@ -809,7 +829,7 @@
         self.assertIn('backup', self.root.folder.objectIds())
         self.assertEqual(
             self.root.folder.backup.get_link_status(),
-            errors.EmptyInvalidTarget())
+            EmptyInvalidTarget())
         self.assertNotIn('backup', ghost.objectIds())