[silva.core.upgrade][Sylvain Viollon] Convert files and image in...

[email protected] Wed, 04 Dec 2013 14:18:15 +0100
Newsgroups gmane.comp.web.zope.silva.cvs
Message-ID <[email protected]>
author:    Sylvain Viollon
date:      Wed Dec 04 14:18:30 2013 +0100
revision:  356:9a812787f7db in silva.core.upgrade
branch:    2.4
details:   https://hg.infrae.com/silva.core.upgrade?cmd=changeset;node=9a812787f7db
modified:  src/silva/core/upgrade/upgrader/tests/test_upgrader230_ghost.py src/silva/core/upgrade/upgrader/tests/test_upgrader230_link.py src/silva/core/upgrade/upgrader/tests/test_upgrader300_hide_from_tocs.py src/silva/core/upgrade/upgrader/tests/test_upgrader_file.py src/silva/core/upgrade/upgrader/tests/test_upgrader_ghost.py src/silva/core/upgrade/upgrader/tests/test_upgrader_hide_from_tocs.py src/silva/core/upgrade/upgrader/tests/test_upgrader_image.py src/silva/core/upgrade/upgrader/tests/test_upgrader_link.py src/silva/core/upgrade/upgrader/upgrade_220.py src/silva/core/upgrade/upgrader/upgrade_230.py src/silva/core/upgrade/upgrader/upgrade_236.py
added:     src/silva/core/upgrade/upgrader/tests/test_upgrader_file.py src/silva/core/upgrade/upgrader/tests/test_upgrader_ghost.py src/silva/core/upgrade/upgrader/tests/test_upgrader_hide_from_tocs.py src/silva/core/upgrade/upgrader/tests/test_upgrader_image.py src/silva/core/upgrade/upgrader/tests/test_upgrader_link.py
removed:   src/silva/core/upgrade/upgrader/tests/test_upgrader230_ghost.py src/silva/core/upgrade/upgrader/tests/test_upgrader230_link.py src/silva/core/upgrade/upgrader/tests/test_upgrader300_hide_from_tocs.py
log:       Convert files and image inside ghost folder as ghost asset if it is
	possible.


diffstat:

 src/silva/core/upgrade/upgrader/tests/test_upgrader230_ghost.py          |  140 -------
 src/silva/core/upgrade/upgrader/tests/test_upgrader230_link.py           |  124 ------
 src/silva/core/upgrade/upgrader/tests/test_upgrader300_hide_from_tocs.py |   46 --
 src/silva/core/upgrade/upgrader/tests/test_upgrader_file.py              |  110 ++++++
 src/silva/core/upgrade/upgrader/tests/test_upgrader_ghost.py             |  140 +++++++
 src/silva/core/upgrade/upgrader/tests/test_upgrader_hide_from_tocs.py    |   46 ++
 src/silva/core/upgrade/upgrader/tests/test_upgrader_image.py             |  137 +++++++
 src/silva/core/upgrade/upgrader/tests/test_upgrader_link.py              |  124 ++++++
 src/silva/core/upgrade/upgrader/upgrade_220.py                           |  101 -----
 src/silva/core/upgrade/upgrader/upgrade_230.py                           |   42 +-
 src/silva/core/upgrade/upgrader/upgrade_236.py                           |  182 ++++++++-
 11 files changed, 726 insertions(+), 466 deletions(-)

diffs (1329 lines):

diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader230_ghost.py
--- a/src/silva/core/upgrade/upgrader/tests/test_upgrader230_ghost.py	Wed Oct 09 16:38:43 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2009-2013 Infrae. All rights reserved.
-# See also LICENSE.txt
-
-import unittest
-
-from Acquisition import aq_chain
-
-from zope.component import getUtility
-from zope.interface.verify import verifyObject
-
-from Products.Silva.testing import FunctionalLayer
-from silva.core.upgrade.upgrader.upgrade_230 import ghost_upgrader
-from silva.core.interfaces import IPublicationWorkflow
-from silva.core.references.interfaces import IReferenceService
-from silva.core.references.interfaces import IWeakReferenceValue
-from silva.core.references.interfaces import IDeleteSourceReferenceValue
-
-
-class GhostUpgraderTestCase(unittest.TestCase):
-    """Test upgrader which rewrites links and images to use
-    references.
-    """
-    layer = FunctionalLayer
-
-    def setUp(self):
-        self.root = self.layer.get_application()
-        self.layer.login('editor')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addPublication('publication', 'Publication')
-        factory.manage_addGhostFolder('ghost_folder', None)
-        factory.manage_addMockupVersionedContent('document', 'Document')
-
-        factory = self.root.publication.manage_addProduct['Silva']
-        factory.manage_addGhost('ghost', None)
-
-        version = self.root.publication.ghost.get_editable()
-        version._content_path = ('', 'root', 'document')
-        IPublicationWorkflow(self.root.publication.ghost).publish()
-
-        self.root.ghost_folder._content_path = ('', 'root', 'publication')
-        factory = self.root.ghost_folder.manage_addProduct['Silva']
-        factory.manage_addGhost('ghost', None)
-        version = self.root.ghost_folder.ghost.get_editable()
-        version._content_path = ('', 'root', 'document')
-        IPublicationWorkflow(self.root.ghost_folder.ghost).publish()
-
-    def test_upgrade_ghost(self):
-        version = self.root.publication.ghost.get_viewable()
-        self.assertIsNot(version, None)
-        self.assertTrue(ghost_upgrader.validate(version))
-        self.assertEqual(ghost_upgrader.upgrade(version), version)
-
-        self.assertFalse(ghost_upgrader.validate(version))
-        self.assertEqual(
-            version.get_haunted(),
-            self.root.document)
-        self.assertEquals(
-            aq_chain(self.root.document),
-            aq_chain(version.get_haunted()))
-
-        service = getUtility(IReferenceService)
-        reference = service.get_reference(version, name=u"haunted")
-        self.assertTrue(verifyObject(IWeakReferenceValue, reference))
-        self.assertEquals(
-            aq_chain(reference.source),
-            aq_chain(version))
-
-    def test_upgrade_ghost_invalid_path(self):
-        version = self.root.publication.ghost.get_viewable()
-        self.assertIsNot(version, None)
-        version._content_path = ('', 'root', 'root', 'document')
-        self.assertTrue(ghost_upgrader.validate(version))
-        self.assertEqual(ghost_upgrader.upgrade(version), version)
-
-        # This didn't change anything, but didn't break.
-        self.assertTrue(ghost_upgrader.validate(version))
-        self.assertEqual(
-            version.get_haunted(),
-            None)
-
-    def test_upgrade_ghost_unexisting_path(self):
-        version = self.root.publication.ghost.get_viewable()
-        self.assertIsNot(version, None)
-        version._content_path = ('', 'root', 'lala')
-        self.assertTrue(ghost_upgrader.validate(version))
-        self.assertEqual(ghost_upgrader.upgrade(version), version)
-
-        # This didn't change anything, but didn't break.
-        self.assertTrue(ghost_upgrader.validate(version))
-        self.assertEqual(
-            version.get_haunted(),
-            None)
-
-    def test_upgrade_ghost_in_ghost_folder(self):
-        version = self.root.ghost_folder.ghost.get_viewable()
-        self.assertIsNot(version, None)
-        self.assertTrue(ghost_upgrader.validate(version))
-        self.assertEqual(ghost_upgrader.upgrade(version), version)
-
-        self.assertFalse(ghost_upgrader.validate(version))
-        self.assertEqual(
-            self.root.document,
-            version.get_haunted())
-        self.assertEquals(
-            aq_chain(self.root.document),
-            aq_chain(version.get_haunted()))
-
-        service = getUtility(IReferenceService)
-        reference = service.get_reference(version, name=u"haunted")
-        self.assertTrue(verifyObject(IDeleteSourceReferenceValue, reference))
-        self.assertEquals(
-            aq_chain(reference.source),
-            aq_chain(version))
-
-    def test_upgrade_ghost_folder(self):
-        folder = self.root.ghost_folder
-        self.assertTrue(ghost_upgrader.validate(folder))
-        self.assertEqual(ghost_upgrader.upgrade(folder), folder)
-
-        self.assertFalse(ghost_upgrader.validate(folder))
-        self.assertEquals(
-            self.root.publication,
-            folder.get_haunted())
-        self.assertEquals(
-            aq_chain(self.root.publication),
-            aq_chain(folder.get_haunted()))
-
-        service = getUtility(IReferenceService)
-        reference = service.get_reference(folder, name=u"haunted")
-        self.assertTrue(verifyObject(IWeakReferenceValue, reference))
-        self.assertEquals(
-            aq_chain(reference.source),
-            aq_chain(folder))
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(GhostUpgraderTestCase))
-    return suite
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader230_link.py
--- a/src/silva/core/upgrade/upgrader/tests/test_upgrader230_link.py	Wed Oct 09 16:38:43 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,124 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2009-2013 Infrae. All rights reserved.
-# See also LICENSE.txt
-
-import unittest
-
-from Products.Silva.testing import FunctionalLayer
-from silva.core.upgrade.upgrader.upgrade_230 import link_upgrader, resolve_path
-
-
-class LinkVersionUpgraderTestCase(unittest.TestCase):
-    """Test upgrader relative links to references
-    """
-    layer = FunctionalLayer
-
-    def setUp(self):
-        self.root = self.layer.get_application()
-        self.layer.login('editor')
-        factory = self.root.manage_addProduct['Silva']
-        factory.manage_addLink('link', 'Link', url="http://www.google.com")
-        self.link = self.root.link
-        self.version = self.link.get_editable()
-
-        factory.manage_addPublication('publication', 'Publication')
-        self.publication = self.root.publication
-        factory = self.root.publication.manage_addProduct['Silva']
-        factory.manage_addFile('file', 'File')
-
-    def test_resolve_path_absolute_link_spaces(self):
-        """resolve_path should work event if there are spaces in the
-        input.
-        """
-        self.assertEqual(
-            resolve_path(' http://www.google.com/',
-                         '/root/publication',
-                         self.root),
-            ('http://www.google.com/', None, None))
-        self.assertEqual(
-            resolve_path(' http://www.google.com/ ',
-                         '/root/publication',
-                         self.root),
-            ('http://www.google.com/', None, None))
-
-    def test_not_validate_absolute_link(self):
-        self.version._url = 'http://www.google.com/'
-        self.assertFalse(link_upgrader.validate(self.version))
-
-    def test_not_validate_absolute_link_spaces(self):
-        """An absolute URL should not validate, even if there are
-        spaces before or after it.
-        """
-        self.version._url = ' http://www.google.com/'
-        self.assertFalse(link_upgrader.validate(self.version))
-
-    def test_not_validate_relative_set(self):
-        """If the link is already convert, it should not validate
-        (there is already _relative set).
-        """
-        self.version._relative = False
-        self.assertFalse(link_upgrader.validate(self.version))
-
-    def test_validate_when_relative(self):
-        self.version._url = '/root/publication/file'
-        self.assertTrue(link_upgrader.validate(self.version))
-        self.version._url = 'publication/file'
-        self.assertTrue(link_upgrader.validate(self.version))
-
-    def test_root_link(self):
-        self.version._url = '/root/publication/file'
-        link_upgrader.upgrade(self.version)
-        self.assertEquals(
-            self.version.get_target(),
-            self.root.publication.file)
-
-    def test_root_link_spaces_post(self):
-        """A path to a Silva content should work even if there are
-        spaces at the end of it.
-        """
-        self.version._url = '/root/publication/file '
-        link_upgrader.upgrade(self.version)
-        self.assertEquals(
-            self.version.get_target(),
-            self.root.publication.file)
-
-    def test_root_link_spaces_pre(self):
-        """A path to a Silva content should work event if there are
-        spaces in front of it.
-        """
-        self.version._url = ' /root/publication/file'
-        link_upgrader.upgrade(self.version)
-        self.assertEquals(
-            self.version.get_target(),
-            self.root.publication.file)
-
-    def test_root_link_without_root(self):
-        """A common case when the root is removed with Apache rewrite
-        rules.
-        """
-        self.version._url = '/publication/file'
-        link_upgrader.upgrade(self.version)
-        self.assertEquals(
-            self.version.get_target(),
-            self.root.publication.file)
-
-    def test_relative_to_self(self):
-        self.version._url = '../root/publication/file'
-        link_upgrader.upgrade(self.version)
-        self.assertEquals(
-            self.version.get_target(),
-            self.root.publication.file)
-
-    def test_relative_to_not_exists(self):
-        self.version._url = '/root/doesnotexists'
-        link_upgrader.upgrade(self.version)
-        self.assertEquals(
-            self.version.get_target(),
-            None)
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(LinkVersionUpgraderTestCase))
-    return suite
-
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader300_hide_from_tocs.py
--- a/src/silva/core/upgrade/upgrader/tests/test_upgrader300_hide_from_tocs.py	Wed Oct 09 16:38:43 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-# -*- coding: utf-8 -*-
-# Copyright (c) 2012-2013 Infrae. All rights reserved.
-# See also LICENSE.txt
-
-
-import unittest
-
-from zope.component import getUtility
-
-from Products.Silva.testing import FunctionalLayer, Transaction
-from Products.SilvaMetadata.interfaces import IMetadataService
-from silva.core.upgrade.upgrader.upgrade_300 import hide_from_toc_upgrader
-
-SILVA_EXTRA = "http://infrae.com/namespace/metadata/silva-extra"
-
-
-class HideFromTOCUpgraderTestCase(unittest.TestCase):
-    """Test upgrader move hide_from_tocs metadata to silva-settings
-    """
-    layer = FunctionalLayer
-
-    def setUp(self):
-        self.root = self.layer.get_application()
-        with Transaction():
-            self.layer.login('manager')
-            factory = self.root.manage_addProduct['Silva']
-            factory.manage_addFolder('folder', 'Folder')
-
-    def test_hide_from_toc(self):
-        service = getUtility(IMetadataService)
-        self.assertFalse(hide_from_toc_upgrader.validate(self.root.folder))
-        mset = self.root.folder.__annotations__[SILVA_EXTRA]
-        mset['hide_from_tocs'] = 'hide'
-        self.assertTrue(hide_from_toc_upgrader.validate(self.root.folder))
-        self.assertEquals(
-            hide_from_toc_upgrader.upgrade(self.root.folder),
-            self.root.folder)
-        self.assertFalse(hide_from_toc_upgrader.validate(self.root.folder))
-        self.assertEqual('hide', service.getMetadataValue(
-            self.root.folder, 'silva-settings', 'hide_from_tocs'))
-
-
-def test_suite():
-    suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(HideFromTOCUpgraderTestCase))
-    return suite
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader_file.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/core/upgrade/upgrader/tests/test_upgrader_file.py	Wed Dec 04 14:18:30 2013 +0100
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+import unittest
+
+from zope.interface.verify import verifyObject
+
+from Products.Silva.testing import FunctionalLayer
+from silva.core.interfaces import IBlobFile, IFile, IGhostAsset
+from silva.core.upgrade.upgrader.upgrade_236 import file_upgrader
+
+
+class FileUpgraderTestCase(unittest.TestCase):
+    layer = FunctionalLayer
+
+    def setUp(self):
+        self.root = self.layer.get_application()
+        self.layer.login('manager')
+
+    def test_file(self):
+        factory = self.root.manage_addProduct['Silva']
+        with self.layer.open_fixture('silva.png') as data:
+            factory.manage_addFile('file', 'File', data)
+        data = self.root._getOb('file')
+
+        self.assertTrue(IFile.providedBy(data))
+        self.assertFalse(IBlobFile.providedBy(data))
+        self.assertTrue(file_upgrader.validate(data))
+
+        result = file_upgrader.upgrade(data)
+        self.assertNotEqual(result, data)
+        self.assertTrue(verifyObject(IBlobFile, result))
+        self.assertEqual(self.root._getOb('file'), result)
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'file.png')
+
+    def test_file_in_valid_ghost_folder(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addFolder('folder', 'Folder')
+        factory.manage_addGhostFolder('ghost', None, haunted=self.root.folder)
+        factory = self.root.folder.manage_addProduct['Silva']
+        with self.layer.open_fixture('silva.png') as data:
+            factory.manage_addFile('file', 'File', data)
+        factory = self.root.ghost.manage_addProduct['Silva']
+        with self.layer.open_fixture('silva.png') as data:
+            factory.manage_addFile('file', 'File', data)
+        data = self.root.ghost._getOb('file')
+
+        self.assertTrue(IFile.providedBy(data))
+        self.assertFalse(IBlobFile.providedBy(data))
+        self.assertTrue(file_upgrader.validate(data))
+
+        result = file_upgrader.upgrade(data)
+        self.assertNotEqual(result, data)
+
+        # The file have been replaced with a ghost asset to the original one.
+        self.assertEqual(self.root.ghost._getOb('file'), result)
+        self.assertTrue(verifyObject(IGhostAsset, result))
+        self.assertEqual(result.get_link_status(), None)
+        self.assertEqual(result.get_haunted(), self.root.folder.file)
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'file.png')
+
+    def test_file_in_valid_ghost_folder_missing_original(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addFolder('folder', 'Folder')
+        factory.manage_addGhostFolder('ghost', None, haunted=self.root.folder)
+        factory = self.root.folder.manage_addProduct['Silva']
+        factory.manage_addIndexer('file', 'File') # This is not a file.
+        factory = self.root.ghost.manage_addProduct['Silva']
+        with self.layer.open_fixture('silva.png') as data:
+            factory.manage_addFile('file', 'File', data)
+        data = self.root.ghost._getOb('file')
+
+        self.assertTrue(IFile.providedBy(data))
+        self.assertFalse(IBlobFile.providedBy(data))
+        self.assertTrue(file_upgrader.validate(data))
+
+        result = file_upgrader.upgrade(data)
+        self.assertNotEqual(result, data)
+        self.assertTrue(verifyObject(IBlobFile, result))
+        self.assertEqual(self.root.ghost._getOb('file'), result)
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'file.png')
+
+    def test_file_in_broken_ghost_folder(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addGhostFolder('ghost', None,)
+        factory = self.root.ghost.manage_addProduct['Silva']
+        with self.layer.open_fixture('silva.png') as data:
+            factory.manage_addFile('file', 'File', data)
+        data = self.root.ghost._getOb('file')
+
+        self.assertTrue(IFile.providedBy(data))
+        self.assertFalse(IBlobFile.providedBy(data))
+        self.assertTrue(file_upgrader.validate(data))
+
+        result = file_upgrader.upgrade(data)
+        self.assertNotEqual(result, data)
+        self.assertTrue(verifyObject(IBlobFile, result))
+        self.assertEqual(self.root.ghost._getOb('file'), result)
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'file.png')
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(FileUpgraderTestCase))
+    return suite
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader_ghost.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/core/upgrade/upgrader/tests/test_upgrader_ghost.py	Wed Dec 04 14:18:30 2013 +0100
@@ -0,0 +1,140 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+import unittest
+
+from Acquisition import aq_chain
+
+from zope.component import getUtility
+from zope.interface.verify import verifyObject
+
+from Products.Silva.testing import FunctionalLayer
+from silva.core.interfaces import IPublicationWorkflow
+from silva.core.references.interfaces import IDeleteSourceReferenceValue
+from silva.core.references.interfaces import IReferenceService
+from silva.core.references.interfaces import IWeakReferenceValue
+from silva.core.upgrade.upgrader.upgrade_230 import ghost_upgrader
+
+
+class GhostUpgraderTestCase(unittest.TestCase):
+    """Test upgrader which rewrites links and images to use
+    references.
+    """
+    layer = FunctionalLayer
+
+    def setUp(self):
+        self.root = self.layer.get_application()
+        self.layer.login('manager')
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addPublication('publication', 'Publication')
+        factory.manage_addGhostFolder('ghost_folder', None)
+        factory.manage_addMockupVersionedContent('document', 'Document')
+
+        factory = self.root.publication.manage_addProduct['Silva']
+        factory.manage_addGhost('ghost', None)
+
+        version = self.root.publication.ghost.get_editable()
+        version._content_path = ('', 'root', 'document')
+        IPublicationWorkflow(self.root.publication.ghost).publish()
+
+        self.root.ghost_folder._content_path = ('', 'root', 'publication')
+        factory = self.root.ghost_folder.manage_addProduct['Silva']
+        factory.manage_addGhost('ghost', None)
+        version = self.root.ghost_folder.ghost.get_editable()
+        version._content_path = ('', 'root', 'document')
+        IPublicationWorkflow(self.root.ghost_folder.ghost).publish()
+
+    def test_ghost(self):
+        version = self.root.publication.ghost.get_viewable()
+        self.assertIsNot(version, None)
+        self.assertTrue(ghost_upgrader.validate(version))
+        self.assertEqual(ghost_upgrader.upgrade(version), version)
+
+        self.assertFalse(ghost_upgrader.validate(version))
+        self.assertEqual(
+            version.get_haunted(),
+            self.root.document)
+        self.assertEquals(
+            aq_chain(self.root.document),
+            aq_chain(version.get_haunted()))
+
+        service = getUtility(IReferenceService)
+        reference = service.get_reference(version, name=u"haunted")
+        self.assertTrue(verifyObject(IWeakReferenceValue, reference))
+        self.assertEquals(
+            aq_chain(reference.source),
+            aq_chain(version))
+
+    def test_ghost_invalid_path(self):
+        version = self.root.publication.ghost.get_viewable()
+        self.assertIsNot(version, None)
+        version._content_path = ('', 'root', 'root', 'document')
+        self.assertTrue(ghost_upgrader.validate(version))
+        self.assertEqual(ghost_upgrader.upgrade(version), version)
+
+        # This didn't change anything, but didn't break.
+        self.assertTrue(ghost_upgrader.validate(version))
+        self.assertEqual(
+            version.get_haunted(),
+            None)
+
+    def test_ghost_unexisting_path(self):
+        version = self.root.publication.ghost.get_viewable()
+        self.assertIsNot(version, None)
+        version._content_path = ('', 'root', 'lala')
+        self.assertTrue(ghost_upgrader.validate(version))
+        self.assertEqual(ghost_upgrader.upgrade(version), version)
+
+        # This didn't change anything, but didn't break.
+        self.assertTrue(ghost_upgrader.validate(version))
+        self.assertEqual(
+            version.get_haunted(),
+            None)
+
+    def test_ghost_in_ghost_folder(self):
+        version = self.root.ghost_folder.ghost.get_viewable()
+        self.assertIsNot(version, None)
+        self.assertTrue(ghost_upgrader.validate(version))
+        self.assertEqual(ghost_upgrader.upgrade(version), version)
+
+        self.assertFalse(ghost_upgrader.validate(version))
+        self.assertEqual(
+            self.root.document,
+            version.get_haunted())
+        self.assertEquals(
+            aq_chain(self.root.document),
+            aq_chain(version.get_haunted()))
+
+        service = getUtility(IReferenceService)
+        reference = service.get_reference(version, name=u"haunted")
+        self.assertTrue(verifyObject(IDeleteSourceReferenceValue, reference))
+        self.assertEquals(
+            aq_chain(reference.source),
+            aq_chain(version))
+
+    def test_ghost_folder(self):
+        folder = self.root.ghost_folder
+        self.assertTrue(ghost_upgrader.validate(folder))
+        self.assertEqual(ghost_upgrader.upgrade(folder), folder)
+
+        self.assertFalse(ghost_upgrader.validate(folder))
+        self.assertEquals(
+            self.root.publication,
+            folder.get_haunted())
+        self.assertEquals(
+            aq_chain(self.root.publication),
+            aq_chain(folder.get_haunted()))
+
+        service = getUtility(IReferenceService)
+        reference = service.get_reference(folder, name=u"haunted")
+        self.assertTrue(verifyObject(IWeakReferenceValue, reference))
+        self.assertEquals(
+            aq_chain(reference.source),
+            aq_chain(folder))
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(GhostUpgraderTestCase))
+    return suite
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader_hide_from_tocs.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/core/upgrade/upgrader/tests/test_upgrader_hide_from_tocs.py	Wed Dec 04 14:18:30 2013 +0100
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2012-2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+
+import unittest
+
+from zope.component import getUtility
+
+from Products.Silva.testing import FunctionalLayer, Transaction
+from Products.SilvaMetadata.interfaces import IMetadataService
+from silva.core.upgrade.upgrader.upgrade_300 import hide_from_toc_upgrader
+
+SILVA_EXTRA = "http://infrae.com/namespace/metadata/silva-extra"
+
+
+class HideFromTOCUpgraderTestCase(unittest.TestCase):
+    """Test upgrader move hide_from_tocs metadata to silva-settings
+    """
+    layer = FunctionalLayer
+
+    def setUp(self):
+        self.root = self.layer.get_application()
+        with Transaction():
+            self.layer.login('manager')
+            factory = self.root.manage_addProduct['Silva']
+            factory.manage_addFolder('folder', 'Folder')
+
+    def test_hide_from_toc(self):
+        service = getUtility(IMetadataService)
+        self.assertFalse(hide_from_toc_upgrader.validate(self.root.folder))
+        mset = self.root.folder.__annotations__[SILVA_EXTRA]
+        mset['hide_from_tocs'] = 'hide'
+        self.assertTrue(hide_from_toc_upgrader.validate(self.root.folder))
+        self.assertEquals(
+            hide_from_toc_upgrader.upgrade(self.root.folder),
+            self.root.folder)
+        self.assertFalse(hide_from_toc_upgrader.validate(self.root.folder))
+        self.assertEqual('hide', service.getMetadataValue(
+            self.root.folder, 'silva-settings', 'hide_from_tocs'))
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(HideFromTOCUpgraderTestCase))
+    return suite
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader_image.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/core/upgrade/upgrader/tests/test_upgrader_image.py	Wed Dec 04 14:18:30 2013 +0100
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+import unittest
+
+from zope.interface.verify import verifyObject
+from OFS.Image import Image
+from Products.Silva.testing import FunctionalLayer
+
+from silva.core.interfaces import IFile, IImage, IGhostAsset
+from silva.core.upgrade.upgrader.upgrade_236 import image_upgrader
+
+
+class ImageUpgraderTestCase(unittest.TestCase):
+    layer = FunctionalLayer
+
+    def setUp(self):
+        self.root = self.layer.get_application()
+        self.layer.login('manager')
+
+    def test_image(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addImage('image', 'Image')
+        image = self.root._getOb('image')
+
+        with self.layer.open_fixture('silva.png') as data:
+            image.hires_image = Image('image', 'Image', data)
+
+        self.assertFalse(IFile.providedBy(image.image))
+        self.assertFalse(IFile.providedBy(image.hires_image))
+        self.assertFalse(IFile.providedBy(image.thumbnail_image))
+        self.assertTrue(image_upgrader.validate(image))
+        result = image_upgrader.upgrade(image)
+        self.assertEqual(result, image)
+
+        # Image is still here, and content have been replaced.
+        self.assertTrue(verifyObject(IImage, result))
+        self.assertTrue(verifyObject(IFile, result.image))
+        self.assertTrue(verifyObject(IFile, result.hires_image))
+        self.assertTrue(verifyObject(IFile, result.thumbnail_image))
+        self.assertEqual(self.root._getOb('image'), result)
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'image.png')
+
+    def test_image_in_valid_ghost_folder(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addFolder('folder', 'Folder')
+        factory.manage_addGhostFolder('ghost', None, haunted=self.root.folder)
+        factory = self.root.folder.manage_addProduct['Silva']
+        with self.layer.open_fixture('silva.png') as data:
+            factory.manage_addImage('image', 'Image', data)
+
+        factory = self.root.ghost.manage_addProduct['Silva']
+        factory.manage_addImage('image', 'Image')
+        image = self.root.ghost._getOb('image')
+        with self.layer.open_fixture('silva.png') as data:
+            image.hires_image = Image('image', 'Image', data)
+
+        self.assertFalse(IFile.providedBy(image.image))
+        self.assertFalse(IFile.providedBy(image.hires_image))
+        self.assertFalse(IFile.providedBy(image.thumbnail_image))
+        self.assertTrue(image_upgrader.validate(image))
+
+        result = image_upgrader.upgrade(image)
+        self.assertNotEqual(result, image)
+
+        # The image have been replaced with a ghost asset to the original one.
+        self.assertEqual(self.root.ghost._getOb('image'), result)
+        self.assertTrue(verifyObject(IGhostAsset, result))
+        self.assertEqual(result.get_link_status(), None)
+        self.assertEqual(result.get_haunted(), self.root.folder.image)
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'image.png')
+
+    def test_image_in_valid_ghost_folder_missing_original(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addFolder('folder', 'Folder')
+        factory.manage_addGhostFolder('ghost', None, haunted=self.root.folder)
+        factory = self.root.folder.manage_addProduct['Silva']
+        factory.manage_addIndexer('image', 'Image') # This is not an image.
+        factory = self.root.ghost.manage_addProduct['Silva']
+        factory.manage_addImage('image', 'Image')
+        image = self.root.ghost._getOb('image')
+        with self.layer.open_fixture('silva.png') as data:
+            image.hires_image = Image('image', 'Image', data)
+
+        self.assertFalse(IFile.providedBy(image.image))
+        self.assertFalse(IFile.providedBy(image.hires_image))
+        self.assertFalse(IFile.providedBy(image.thumbnail_image))
+        self.assertTrue(image_upgrader.validate(image))
+
+        result = image_upgrader.upgrade(image)
+        self.assertEqual(result, image)
+
+        # Image should have not been replaced, but have been updated.
+        self.assertEqual(self.root.ghost._getOb('image'), result)
+        self.assertTrue(verifyObject(IImage, result))
+        self.assertTrue(verifyObject(IFile, result.image))
+        self.assertTrue(verifyObject(IFile, result.hires_image))
+        self.assertTrue(verifyObject(IFile, result.thumbnail_image))
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'image.png')
+
+    def test_image_in_broken_ghost_folder(self):
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addGhostFolder('ghost', None)
+        factory = self.root.ghost.manage_addProduct['Silva']
+        factory.manage_addImage('image', 'Image')
+        image = self.root.ghost._getOb('image')
+
+        with self.layer.open_fixture('silva.png') as data:
+            image.hires_image = Image('image', 'Image', data)
+
+        self.assertFalse(IFile.providedBy(image.image))
+        self.assertFalse(IFile.providedBy(image.hires_image))
+        self.assertFalse(IFile.providedBy(image.thumbnail_image))
+        self.assertTrue(image_upgrader.validate(image))
+
+        result = image_upgrader.upgrade(image)
+        self.assertEqual(result, image)
+
+        # Image should have not been replaced, but have been updated.
+        self.assertEqual(self.root.ghost._getOb('image'), result)
+        self.assertTrue(verifyObject(IImage, result))
+        self.assertTrue(verifyObject(IFile, result.image))
+        self.assertTrue(verifyObject(IFile, result.hires_image))
+        self.assertTrue(verifyObject(IFile, result.thumbnail_image))
+        self.assertEqual(result.get_mime_type(), 'image/png')
+        self.assertEqual(result.get_filename(), 'image.png')
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(ImageUpgraderTestCase))
+    return suite
+
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/tests/test_upgrader_link.py
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/silva/core/upgrade/upgrader/tests/test_upgrader_link.py	Wed Dec 04 14:18:30 2013 +0100
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2009-2013 Infrae. All rights reserved.
+# See also LICENSE.txt
+
+import unittest
+
+from Products.Silva.testing import FunctionalLayer
+from silva.core.upgrade.upgrader.upgrade_230 import link_upgrader, resolve_path
+
+
+class LinkVersionUpgraderTestCase(unittest.TestCase):
+    """Test upgrader relative links to references
+    """
+    layer = FunctionalLayer
+
+    def setUp(self):
+        self.root = self.layer.get_application()
+        self.layer.login('editor')
+        factory = self.root.manage_addProduct['Silva']
+        factory.manage_addLink('link', 'Link', url="http://www.google.com")
+        self.link = self.root.link
+        self.version = self.link.get_editable()
+
+        factory.manage_addPublication('publication', 'Publication')
+        self.publication = self.root.publication
+        factory = self.root.publication.manage_addProduct['Silva']
+        factory.manage_addFile('file', 'File')
+
+    def test_resolve_path_absolute_link_spaces(self):
+        """resolve_path should work event if there are spaces in the
+        input.
+        """
+        self.assertEqual(
+            resolve_path(' http://www.google.com/',
+                         '/root/publication',
+                         self.root),
+            ('http://www.google.com/', None, None))
+        self.assertEqual(
+            resolve_path(' http://www.google.com/ ',
+                         '/root/publication',
+                         self.root),
+            ('http://www.google.com/', None, None))
+
+    def test_not_validate_absolute_link(self):
+        self.version._url = 'http://www.google.com/'
+        self.assertFalse(link_upgrader.validate(self.version))
+
+    def test_not_validate_absolute_link_spaces(self):
+        """An absolute URL should not validate, even if there are
+        spaces before or after it.
+        """
+        self.version._url = ' http://www.google.com/'
+        self.assertFalse(link_upgrader.validate(self.version))
+
+    def test_not_validate_relative_set(self):
+        """If the link is already convert, it should not validate
+        (there is already _relative set).
+        """
+        self.version._relative = False
+        self.assertFalse(link_upgrader.validate(self.version))
+
+    def test_validate_when_relative(self):
+        self.version._url = '/root/publication/file'
+        self.assertTrue(link_upgrader.validate(self.version))
+        self.version._url = 'publication/file'
+        self.assertTrue(link_upgrader.validate(self.version))
+
+    def test_root_link(self):
+        self.version._url = '/root/publication/file'
+        link_upgrader.upgrade(self.version)
+        self.assertEquals(
+            self.version.get_target(),
+            self.root.publication.file)
+
+    def test_root_link_spaces_post(self):
+        """A path to a Silva content should work even if there are
+        spaces at the end of it.
+        """
+        self.version._url = '/root/publication/file '
+        link_upgrader.upgrade(self.version)
+        self.assertEquals(
+            self.version.get_target(),
+            self.root.publication.file)
+
+    def test_root_link_spaces_pre(self):
+        """A path to a Silva content should work event if there are
+        spaces in front of it.
+        """
+        self.version._url = ' /root/publication/file'
+        link_upgrader.upgrade(self.version)
+        self.assertEquals(
+            self.version.get_target(),
+            self.root.publication.file)
+
+    def test_root_link_without_root(self):
+        """A common case when the root is removed with Apache rewrite
+        rules.
+        """
+        self.version._url = '/publication/file'
+        link_upgrader.upgrade(self.version)
+        self.assertEquals(
+            self.version.get_target(),
+            self.root.publication.file)
+
+    def test_relative_to_self(self):
+        self.version._url = '../root/publication/file'
+        link_upgrader.upgrade(self.version)
+        self.assertEquals(
+            self.version.get_target(),
+            self.root.publication.file)
+
+    def test_relative_to_not_exists(self):
+        self.version._url = '/root/doesnotexists'
+        link_upgrader.upgrade(self.version)
+        self.assertEquals(
+            self.version.get_target(),
+            None)
+
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(LinkVersionUpgraderTestCase))
+    return suite
+
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/upgrade_220.py
--- a/src/silva/core/upgrade/upgrader/upgrade_220.py	Wed Oct 09 16:38:43 2013 +0200
+++ b/src/silva/core/upgrade/upgrader/upgrade_220.py	Wed Dec 04 14:18:30 2013 +0100
@@ -2,13 +2,11 @@
 # Copyright (c) 2002-2013 Infrae. All rights reserved.
 # See also LICENSE.txt
 
-from cStringIO import StringIO
 import logging
 import os
 
 from ZPublisher.BeforeTraverse import unregisterBeforeTraverse
 from zope.annotation.interfaces import IAnnotations
-from zope.component import getUtility
 from zope.interface import implements
 from zope.location.interfaces import ISite
 from zope.site.hooks import setSite, setHooks
@@ -26,9 +24,7 @@
 
 from silva.core import interfaces
 from silva.core import conf as silvaconf
-from silva.core.interfaces.errors import UpgradeError
 from silva.core.services.catalog import CatalogService
-from silva.core.services.interfaces import ICataloging
 from silva.core.services.interfaces import IMetadataService
 from silva.core.services.interfaces import IExtensionService
 from silva.core.services.interfaces import ICatalogService, IFilesService
@@ -150,103 +146,6 @@
 root_post_upgrader = RootPostUpgrader(VERSION_A1, 'Silva Root')
 
 
-class ImagesUpgrader(BaseUpgrader):
-    """Convert image storage to blob storage.
-    """
-    _guess_buffer_type = None
-
-    tags = {'pre',}
-
-    @property
-    def guess_buffer_type(self):
-        if self._guess_buffer_type is None:
-            self._guess_buffer_type = getUtility(
-                interfaces.IMimeTypeClassifier).guess_buffer_type
-        return self._guess_buffer_type
-
-    def upgrade(self, img):
-        # Add stuff here
-        data = None
-        hires_image = img.hires_image
-        if hires_image is None:
-            hires_image = img.image
-            if hires_image is None:
-                # Can't do anything
-                return img
-        if hires_image.meta_type == 'Image':
-            data = StringIO(str(hires_image.data))
-        elif hires_image.meta_type in ('ExtImage', 'ExtFile'):
-            filename = hires_image.get_filename()
-            try:
-                data = open(filename, 'rb')
-            except IOError:
-                raise UpgradeError(u"Missing file %s." % filename, img)
-        elif hires_image.meta_type == 'Silva File':
-            # Already converted ?
-            return img
-        else:
-            raise UpgradeError(u"Unknown mimetype.", img)
-        data.seek(0)
-        full_data = data.read()
-        data.seek(0)
-        content_type, encoding = self.guess_buffer_type(full_data)
-        if content_type is None or encoding is not None:
-            raise UpgradeError(u"Impossible to detect mimetype.", img)
-        # fix some bug in old Images that could be BMP
-        if img.web_format not in img.web_formats:
-            img.web_format = 'JPEG'
-        img._image_factory('hires_image', data, content_type)
-        try:
-            img._create_derived_images()
-        except ValueError as error:
-            logger.error(error.args[0])
-        data.close()
-        ICataloging(img).reindex()
-        logger.info(u"Update image %s rebuilt.", content_path(img))
-        return img
-
-
-images_upgrader = ImagesUpgrader(VERSION_A1, 'Silva Image')
-
-
-class FilesUpgrader(BaseUpgrader):
-    """Convert storage for a file to blob storage.
-    """
-
-    tags = {'pre',}
-
-    def validate(self, content):
-        if interfaces.IBlobFile.providedBy(content):
-            return False
-        return interfaces.IFile.providedBy(content)
-
-    def upgrade(self, content):
-        identifier = content.getId()
-
-        tmp_identifier = identifier + 'conv_storage'
-        new_file = BlobFile(identifier)
-        container = content.aq_parent
-        if not interfaces.IContainer.providedBy(container):
-            logger.error(u'Invalid file: %s', content_path(content))
-            # Self-autodestruct file.
-            container._delObject(identifier)
-            raise StopIteration
-        container._setObject(tmp_identifier, new_file)
-        new_file = container._getOb(tmp_identifier)
-        self.replace_references(content, new_file)
-        self.replace(content, new_file)
-        new_file.set_file(
-            content.get_file_fd(),
-            content_type=content.get_content_type(),
-            content_encoding=content.get_content_encoding())
-        container._delObject(identifier)
-        container.manage_renameObject(tmp_identifier, identifier)
-        logger.info(u"File %s migrated.", content_path(new_file))
-        return new_file
-
-files_upgrader = FilesUpgrader(VERSION_A1, 'Silva File')
-
-
 
 #-----------------------------------------------------------------------------
 # 2.2.0a1 to 2.2.0a2
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/upgrade_230.py
--- a/src/silva/core/upgrade/upgrader/upgrade_230.py	Wed Oct 09 16:38:43 2013 +0200
+++ b/src/silva/core/upgrade/upgrader/upgrade_230.py	Wed Dec 04 14:18:30 2013 +0100
@@ -18,7 +18,7 @@
 from silva.core.references.interfaces import IReferenceService
 from silva.core.services.interfaces import IContainerPolicyService
 from silva.core.services.interfaces import IMemberService
-from silva.core.upgrade.upgrade import BaseUpgrader, content_path
+from silva.core.upgrade.upgrade import BaseUpgrader, content_path, AnyMetaType
 
 logger = logging.getLogger('silva.core.upgrade')
 
@@ -29,6 +29,7 @@
 
 VERSION_B1='2.3b1'
 VERSION_B2='2.3b2'
+VERSION_B3='2.3b3'              # Added to convert files after ghosts.
 VERSION_FINAL='2.3'
 
 
@@ -198,11 +199,14 @@
         return ghost
 
 
+ghost_upgrader = GhostUpgrader(
+    VERSION_B1, ["Silva Ghost Version", "Silva Ghost Folder"])
+
+
 class VersionedContentUpgrader(BaseUpgrader):
     """Remove cache_data from versioned content as this is not used
     anymore.
     """
-
     tags = {'pre',}
 
     def validate(self, content):
@@ -217,6 +221,8 @@
             del content._cached_data
         return content
 
+cache_upgrader = VersionedContentUpgrader(VERSION_B1, AnyMetaType)
+
 
 class LinkVersionUpgrader(BaseUpgrader):
     """ replace relative links with references
@@ -246,10 +252,20 @@
 
 link_upgrader = LinkVersionUpgrader(VERSION_B1, 'Silva Link Version')
 
-cache_upgrader = VersionedContentUpgrader(
-    VERSION_B1, ['Silva Ghost', 'Silva Link'])
-ghost_upgrader = GhostUpgrader(
-    VERSION_B1, ["Silva Ghost Version", "Silva Ghost Folder"])
+
+class CSVSourceUpgrader(BaseUpgrader):
+
+    tags = {'pre',}
+
+    def upgrade(self, content):
+        from Products.SilvaExternalSources.CSVSource import (
+            reset_parameter_form, reset_table_layout)
+        reset_parameter_form(content)
+        reset_table_layout(content)
+        return content
+
+
+csvsource_upgrader = CSVSourceUpgrader(VERSION_B2, 'Silva CSV Source')
 
 
 class SecondRootUpgrader(BaseUpgrader):
@@ -311,20 +327,6 @@
             delattr(root, '__initialization__')
         return root
 
-
-class CSVSourceUpgrader(BaseUpgrader):
-
-    tags = {'pre',}
-
-    def upgrade(self, content):
-        from Products.SilvaExternalSources.CSVSource import (
-            reset_parameter_form, reset_table_layout)
-        reset_parameter_form(content)
-        reset_table_layout(content)
-        return content
-
-
-csvsource_upgrader = CSVSourceUpgrader(VERSION_B2, 'Silva CSV Source')
 second_root_upgrader = SecondRootUpgrader(VERSION_B2, 'Silva Root')
 
 
diff -r fe6a39f886e1 -r 9a812787f7db src/silva/core/upgrade/upgrader/upgrade_236.py
--- a/src/silva/core/upgrade/upgrader/upgrade_236.py	Wed Oct 09 16:38:43 2013 +0200
+++ b/src/silva/core/upgrade/upgrader/upgrade_236.py	Wed Dec 04 14:18:30 2013 +0100
@@ -3,57 +3,169 @@
 # See also LICENSE.txt
 
 import logging
+import io
 
 from zope.component import getUtility
 from silva.core.interfaces import IMimeTypeClassifier
+from silva.core.interfaces import IContainer, IGhostFolder
+from silva.core.interfaces import IImage, IFile, IBlobFile
 from silva.core.upgrade.upgrade import BaseUpgrader, content_path
+from silva.core.interfaces.errors import UpgradeError
+from silva.core.services.interfaces import ICataloging
+
+from Products.Silva.File import BlobFile
+from Products.Silva.GhostFolder.content import get_factory
 
 logger = logging.getLogger('silva.core.upgrade')
 
-VERSION_SIX='2.3.6'
+VERSION_FINAL='2.3.6'
 
 
-class FileUpgrader(BaseUpgrader):
-    _update_filename = None
 
-    tags = {'pre',}
+class ImagesUpgrader(BaseUpgrader):
+    """Convert image storage to blob storage.
+    """
+    _guess_buffer_type = None
 
     @property
-    def update_filename(self):
-        if self._update_filename is None:
-            self._update_filename = getUtility(
-                IMimeTypeClassifier).guess_filename
-        return self._update_filename
+    def guess_buffer_type(self):
+        if self._guess_buffer_type is None:
+            self._guess_buffer_type = getUtility(
+                IMimeTypeClassifier).guess_buffer_type
+        return self._guess_buffer_type
 
-    def upgrade(self, item):
-        old_filename = item.get_filename()
-        new_filename = self.update_filename(item, item.getId())
-        if old_filename != new_filename:
-            logger.debug('update filename from %s to %s (%s), in %s' % (
-                    old_filename, new_filename,
-                    item.content_type(), content_path(item)))
-        return item
+    def upgrade_to_ghost(self, img):
+        container = img.get_container()
+        if not IGhostFolder.providedBy(container):
+            return None
+        if container.get_link_status() is not None:
+            logger.warning(
+                u"Invalid ghost folder invalid, not transforming %s.",
+                content_path(img))
+            return None
+        img_id = img.getId()
+        container_haunted = container.get_haunted()
+        img_haunted = container_haunted._getOb(img_id, None)
+        if not IImage.providedBy(img_haunted):
+            logger.warning(
+                u"Original found for %s, but is not an image, not transforming it.",
+                content_path(img))
+            return None
+        return get_factory(img_haunted)(
+                    ghost=img,
+                    container=container,
+                    auto_delete=True,
+                    auto_publish=True).modify(img_haunted, img_id).verify()
 
-# Those should be correctly generated in upgrade_220 now.
-#file_upgrader = FileUpgrader(VERSION_SIX, 'Silva File')
+    def upgrade_to_file(self, content, data):
+        data.seek(0)
+        full_data = data.read()
+        data.seek(0)
+        content_type, encoding = self.guess_buffer_type(full_data)
+        if content_type is None or encoding is not None:
+            raise UpgradeError(u"Impossible to detect mimetype.", content)
+        # fix some bug in old Images that could be BMP
+        if content.web_format not in content.web_formats:
+            content.web_format = 'JPEG'
+        content._image_factory('hires_image', data, content_type)
+        try:
+            content._create_derived_images()
+        except ValueError as error:
+            logger.error(error.args[0])
+        data.close()
+        ICataloging(content).reindex()
+        logger.info(u"Image %s rebuilt.", content_path(content))
+        return content
 
+    def upgrade(self, content):
+        ghost = self.upgrade_to_ghost(content)
+        if ghost is not None:
+            return ghost
+        # Verify stored
+        data = None
+        hires_image = content.hires_image
+        if hires_image is None:
+            hires_image = content.image
+            if hires_image is None:
+                # Can't do anything
+                return content
+        if hires_image.meta_type == 'Image':
+            data = io.BytesIO(str(hires_image.data))
+        elif hires_image.meta_type in ('ExtImage', 'ExtFile'):
+            filename = hires_image.get_filename()
+            try:
+                data = open(filename, 'rb')
+            except IOError:
+                raise UpgradeError(u"Missing file %s." % filename, content)
+        elif hires_image.meta_type == 'Silva File':
+            # Already converted ?
+            return content
+        else:
+            raise UpgradeError(u"Unknown image storage.", content)
+        return self.upgrade_to_file(content, data)
 
-class ImageUpgrader(FileUpgrader):
 
-    tags = {'pre',}
+image_upgrader = ImagesUpgrader(VERSION_FINAL, 'Silva Image')
 
-    def upgrade(self, item):
-        for file_id in ('hires_image', 'image', 'thumbnail_image'):
-            item_file = getattr(item, file_id, None)
-            if item_file is None:
-                continue
-            old_filename = item_file.get_filename()
-            new_filename = self.update_filename(item_file, item.getId())
-            if old_filename != new_filename:
-                logger.debug('update filename from %s to %s (%s), in %s' % (
-                        old_filename, new_filename,
-                        item_file.content_type(), content_path(item)))
-        return item
 
-# Those should be correctly generated in upgrade_220 now.
-#image_upgrader = ImageUpgrader(VERSION_SIX, 'Silva Image')
+class FilesUpgrader(BaseUpgrader):
+    """Convert storage for a file to blob storage.
+    """
+
+    def validate(self, content):
+        if IBlobFile.providedBy(content):
+            return False
+        return IFile.providedBy(content)
+
+    def upgrade_to_ghost(self, content):
+        container = content.get_container()
+        if not IGhostFolder.providedBy(container):
+            return None
+        if container.get_link_status() is not None:
+            logger.warning(
+                u"Invalid ghost folder invalid, not transforming %s.",
+                content_path(content))
+            return None
+        file_id = content.getId()
+        container_haunted = container.get_haunted()
+        file_haunted = container_haunted._getOb(file_id, None)
+        if not IFile.providedBy(file_haunted):
+            logger.warning(
+                u"Original found for %s, but is not an file, not transforming it.",
+                content_path(content))
+            return None
+        return get_factory(file_haunted)(
+                    ghost=content,
+                    container=container,
+                    auto_delete=True,
+                    auto_publish=True).modify(file_haunted, file_id).verify()
+
+    def upgrade(self, content):
+        identifier = content.getId()
+        container = content.aq_parent
+        if not IContainer.providedBy(container):
+            logger.error(u'Invalid file: %s', content_path(content))
+            # Self-autodestruct file.
+            container._delObject(identifier)
+            raise StopIteration
+
+        ghost = self.upgrade_to_ghost(content)
+        if ghost is not None:
+            return ghost
+
+        new_file = BlobFile(identifier)
+        tmp_identifier = identifier + 'conv_storage'
+        container._setObject(tmp_identifier, new_file)
+        new_file = container._getOb(tmp_identifier)
+        self.replace_references(content, new_file)
+        self.replace(content, new_file)
+        new_file.set_file(
+            content.get_file_fd(),
+            content_type=content.get_content_type(),
+            content_encoding=content.get_content_encoding())
+        container._delObject(identifier)
+        container.manage_renameObject(tmp_identifier, identifier)
+        logger.info(u"File %s rebuilt.", content_path(new_file))
+        return new_file
+
+file_upgrader = FilesUpgrader(VERSION_FINAL, 'Silva File')